All 5 phases of the open-source Decal rebuild: Phase 1: 14 decompiled .NET projects (Interop.*, Adapter, FileService, DecalUtil) Phase 2: 10 native DLLs rewritten as C# COM servers with matching GUIDs - DecalDat, DHS, SpellFilter, DecalInput, DecalNet, DecalFilters - Decal.Core, DecalControls, DecalRender, D3DService Phase 3: C++ shims for Inject.DLL (D3D9 hooking) and LauncherHook.DLL Phase 4: DenAgent WinForms tray application Phase 5: WiX installer and build script 25 C# projects building with 0 errors. Native C++ projects require VS 2022 + Windows SDK (x86). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
120 lines
3.9 KiB
C#
120 lines
3.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.InteropServices;
|
|
using Decal.Interop.Input;
|
|
|
|
namespace Decal.DecalInput
|
|
{
|
|
[ComVisible(true)]
|
|
[Guid("F183506A-3664-49D6-8CA4-CFD295F2811D")]
|
|
[ClassInterface(ClassInterfaceType.None)]
|
|
[ComSourceInterfaces(typeof(IHotkeyEvents))]
|
|
[ProgId("DecalInput.Hotkey")]
|
|
public class HotkeyImpl : IHotkey
|
|
{
|
|
private object _tag;
|
|
private int _vk = -1;
|
|
private bool _enabled;
|
|
|
|
public event IHotkeyEvents_HotkeyEventHandler Hotkey;
|
|
|
|
public object Tag
|
|
{
|
|
get => _tag;
|
|
set => _tag = value;
|
|
}
|
|
|
|
public string Key
|
|
{
|
|
get
|
|
{
|
|
if (_vk == -1) return string.Empty;
|
|
string name = KeyNames.NameFromVK(_vk);
|
|
return name != null ? "{" + name + "}" : ((char)_vk).ToString();
|
|
}
|
|
set
|
|
{
|
|
string s = value?.ToUpperInvariant();
|
|
if (string.IsNullOrEmpty(s)) return;
|
|
|
|
if (s[0] == '{')
|
|
{
|
|
int end = s.IndexOf('}');
|
|
if (end > 1)
|
|
{
|
|
string keyName = s.Substring(1, end - 1);
|
|
int vk = KeyNames.VKFromName(keyName);
|
|
if (vk >= 0) _vk = vk;
|
|
}
|
|
}
|
|
else if (s.Length == 1)
|
|
{
|
|
_vk = s[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool Enabled
|
|
{
|
|
get => _enabled;
|
|
set
|
|
{
|
|
if (value == _enabled) return;
|
|
_enabled = value;
|
|
|
|
if (InputServiceImpl.Instance != null)
|
|
{
|
|
if (_enabled)
|
|
InputServiceImpl.Instance.RegisterHotkey(this);
|
|
else
|
|
InputServiceImpl.Instance.UnregisterHotkey(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void FireHotkey()
|
|
{
|
|
Hotkey?.Invoke((Hotkey)(object)this);
|
|
}
|
|
}
|
|
|
|
internal static class KeyNames
|
|
{
|
|
private static readonly Dictionary<string, int> _nameToVK = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
{"BACKSPACE", 0x08}, {"BS", 0x08}, {"BKSP", 0x08},
|
|
{"CAPSLOCK", 0x14}, {"DELETE", 0x2E}, {"DEL", 0x2E},
|
|
{"DOWN", 0x28}, {"END", 0x23}, {"ENTER", 0x0D},
|
|
{"ESC", 0x1B}, {"HELP", 0x2F}, {"HOME", 0x24},
|
|
{"INS", 0x2D}, {"INSERT", 0x2D}, {"LEFT", 0x25},
|
|
{"NUMLOCK", 0x90}, {"PGDN", 0x22}, {"PGUP", 0x21},
|
|
{"PRTSC", 0x2C}, {"RIGHT", 0x27}, {"SCROLLLOCK", 0x91},
|
|
{"TAB", 0x09}, {"UP", 0x26},
|
|
{"F1", 0x70}, {"F2", 0x71}, {"F3", 0x72}, {"F4", 0x73},
|
|
{"F5", 0x74}, {"F6", 0x75}, {"F7", 0x76}, {"F8", 0x77},
|
|
{"F9", 0x78}, {"F10", 0x79}, {"F11", 0x7A}, {"F12", 0x7B},
|
|
{"F13", 0x7C}, {"F14", 0x7D}, {"F15", 0x7E}, {"F16", 0x7F},
|
|
{"+", 0x6B}, {"SHIFT", 0x10}, {"CTRL", 0x11}, {"ALT", 0x12},
|
|
{"LBUTTON", 0x01}, {"RBUTTON", 0x02}
|
|
};
|
|
|
|
private static readonly Dictionary<int, string> _vkToName;
|
|
|
|
static KeyNames()
|
|
{
|
|
_vkToName = new Dictionary<int, string>();
|
|
// Build reverse mapping, preferring longer names
|
|
foreach (var kvp in _nameToVK)
|
|
{
|
|
if (!_vkToName.ContainsKey(kvp.Value) || kvp.Key.Length > _vkToName[kvp.Value].Length)
|
|
_vkToName[kvp.Value] = kvp.Key;
|
|
}
|
|
}
|
|
|
|
public static int VKFromName(string name) =>
|
|
_nameToVK.TryGetValue(name, out int vk) ? vk : -1;
|
|
|
|
public static string NameFromVK(int vk) =>
|
|
_vkToName.TryGetValue(vk, out string name) ? name : null;
|
|
}
|
|
}
|