mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2026-07-05 02:29:05 +00:00
general cleanup for some warnings in the project and some developer annoyances. Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/161
235 lines
7.6 KiB
C#
235 lines
7.6 KiB
C#
using Avalonia.Input;
|
|
using Ryujinx.Ava.Common.Locale;
|
|
using Ryujinx.Ava.Input;
|
|
using Ryujinx.Common.Configuration;
|
|
using Ryujinx.Common.Logging;
|
|
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Text.Json;
|
|
using AvaPhysicalKey = Avalonia.Input.PhysicalKey;
|
|
using ConfigPhysicalKey = Ryujinx.Common.Configuration.Hid.PhysicalKey;
|
|
using InputKey = Ryujinx.Input.Key;
|
|
|
|
namespace Ryujinx.Ava.UI.Helpers
|
|
{
|
|
internal static class PhysicalKeyLabelHelper
|
|
{
|
|
private const string ObservedLabelsFileName = "keyboard_layout_labels.json";
|
|
private static readonly ConcurrentDictionary<ConfigPhysicalKey, string> _observedLayoutLabels = new();
|
|
private static readonly object _observedLayoutLabelsLock = new();
|
|
private static readonly JsonSerializerOptions _serializerOptions = new()
|
|
{
|
|
WriteIndented = true,
|
|
AllowTrailingCommas = true,
|
|
ReadCommentHandling = JsonCommentHandling.Skip
|
|
};
|
|
private static bool _observedLayoutLabelsLoaded;
|
|
public static event Action LabelsChanged;
|
|
|
|
public static string GetDisplayString(ConfigPhysicalKey key)
|
|
{
|
|
EnsureObservedLayoutLabelsLoaded();
|
|
|
|
if (KeyboardLayoutLocaleHelper.TryGetPhysicalLabel(key, out string localizedLabel))
|
|
{
|
|
return localizedLabel;
|
|
}
|
|
|
|
if (_observedLayoutLabels.TryGetValue(key, out string observedLabel))
|
|
{
|
|
return observedLabel;
|
|
}
|
|
|
|
if (TryGetFallbackPrintableKeyLabel(key, out string label))
|
|
{
|
|
return label;
|
|
}
|
|
|
|
return key.ToString();
|
|
}
|
|
|
|
public static void ObserveKeyPress(object sender, KeyEventArgs args)
|
|
{
|
|
EnsureObservedLayoutLabelsLoaded();
|
|
|
|
if (args.KeyModifiers != KeyModifiers.None)
|
|
{
|
|
return;
|
|
}
|
|
|
|
InputKey inputKey = AvaloniaKeyboardMappingHelper.ToInputKey(args.PhysicalKey);
|
|
if (!TryConvertToConfigPhysicalKey(inputKey, out ConfigPhysicalKey physicalKey) ||
|
|
KeyboardLayoutLocaleHelper.TryGetPhysicalLocaleKey(physicalKey, out _))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (TryNormalizeObservedPrintableLabel(args.KeySymbol, out string label))
|
|
{
|
|
if (IsCapsLockOn() && !char.IsLetter(label[0]))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (_observedLayoutLabels.TryGetValue(physicalKey, out string existingLabel) && existingLabel == label)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_observedLayoutLabels[physicalKey] = label;
|
|
SaveObservedLayoutLabels();
|
|
LabelsChanged?.Invoke();
|
|
}
|
|
}
|
|
|
|
private static void EnsureObservedLayoutLabelsLoaded()
|
|
{
|
|
if (_observedLayoutLabelsLoaded)
|
|
{
|
|
return;
|
|
}
|
|
|
|
lock (_observedLayoutLabelsLock)
|
|
{
|
|
if (_observedLayoutLabelsLoaded)
|
|
{
|
|
return;
|
|
}
|
|
|
|
string labelsPath = GetObservedLabelsPath();
|
|
if (!File.Exists(labelsPath))
|
|
{
|
|
_observedLayoutLabelsLoaded = true;
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
string labelsJson = File.ReadAllText(labelsPath);
|
|
Dictionary<string, string> labels = JsonSerializer.Deserialize<Dictionary<string, string>>(labelsJson, _serializerOptions);
|
|
|
|
if (labels != null)
|
|
{
|
|
foreach ((string key, string value) in labels)
|
|
{
|
|
if (Enum.TryParse(key, out ConfigPhysicalKey physicalKey) &&
|
|
!string.IsNullOrEmpty(value))
|
|
{
|
|
_observedLayoutLabels[physicalKey] = value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.Warning?.Print(LogClass.UI, $"Unable to load observed keyboard layout labels from '{labelsPath}': {ex.Message}");
|
|
}
|
|
finally
|
|
{
|
|
_observedLayoutLabelsLoaded = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void SaveObservedLayoutLabels()
|
|
{
|
|
lock (_observedLayoutLabelsLock)
|
|
{
|
|
try
|
|
{
|
|
Dictionary<string, string> labels = new();
|
|
|
|
foreach ((ConfigPhysicalKey key, string value) in _observedLayoutLabels)
|
|
{
|
|
labels[key.ToString()] = value;
|
|
}
|
|
|
|
File.WriteAllText(GetObservedLabelsPath(), JsonSerializer.Serialize(labels, _serializerOptions));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.Warning?.Print(LogClass.UI, $"Unable to save observed keyboard layout labels: {ex.Message}");
|
|
}
|
|
}
|
|
}
|
|
|
|
private static string GetObservedLabelsPath()
|
|
{
|
|
return Path.Combine(AppDataManager.BaseDirPath, ObservedLabelsFileName);
|
|
}
|
|
|
|
private static bool TryGetFallbackPrintableKeyLabel(ConfigPhysicalKey key, out string label)
|
|
{
|
|
// The legacy enum name for the ISO extra key is misleading, so give it a distinct physical label.
|
|
if (key == ConfigPhysicalKey.Grave)
|
|
{
|
|
label = "<>";
|
|
return true;
|
|
}
|
|
|
|
if (!AvaloniaKeyboardMappingHelper.TryGetAvaPhysicalKey((InputKey)(int)key, out AvaPhysicalKey avaPhysicalKey))
|
|
{
|
|
label = string.Empty;
|
|
return false;
|
|
}
|
|
|
|
label = PhysicalKeyExtensions.ToQwertyKeySymbol(avaPhysicalKey, false);
|
|
|
|
if (string.IsNullOrEmpty(label) || label.Length != 1 || char.IsControl(label[0]))
|
|
{
|
|
label = string.Empty;
|
|
return false;
|
|
}
|
|
|
|
if (char.IsLetter(label[0]))
|
|
{
|
|
label = char.ToUpperInvariant(label[0]).ToString();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static bool IsCapsLockOn()
|
|
{
|
|
try
|
|
{
|
|
return OperatingSystem.IsWindows() && Console.CapsLock;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.Debug?.Print(LogClass.UI, $"CapsLock state query failed: {ex.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static bool TryNormalizeObservedPrintableLabel(string keySymbol, out string label)
|
|
{
|
|
if (string.IsNullOrEmpty(keySymbol) || keySymbol.Length != 1 || char.IsControl(keySymbol[0]))
|
|
{
|
|
label = string.Empty;
|
|
return false;
|
|
}
|
|
|
|
label = char.IsLetter(keySymbol[0])
|
|
? char.ToUpperInvariant(keySymbol[0]).ToString()
|
|
: keySymbol;
|
|
|
|
return true;
|
|
}
|
|
|
|
private static bool TryConvertToConfigPhysicalKey(InputKey key, out ConfigPhysicalKey physicalKey)
|
|
{
|
|
if (key is >= InputKey.Unknown and < InputKey.Count)
|
|
{
|
|
physicalKey = (ConfigPhysicalKey)(int)key;
|
|
return true;
|
|
}
|
|
|
|
physicalKey = ConfigPhysicalKey.Unknown;
|
|
return false;
|
|
}
|
|
}
|
|
}
|