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>
161 lines
5.1 KiB
C#
161 lines
5.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.InteropServices;
|
|
using Decal.Interop.Core;
|
|
using Decal.Interop.DHS;
|
|
|
|
namespace Decal.DHS
|
|
{
|
|
internal class RegisteredHotkey
|
|
{
|
|
public string PluginCookie;
|
|
public HotkeyImpl Hotkey;
|
|
}
|
|
|
|
[ComVisible(true)]
|
|
[Guid("6B6B9FA8-37DE-4FA3-8C60-52BD6A2F9855")]
|
|
[ClassInterface(ClassInterfaceType.None)]
|
|
[ComSourceInterfaces(typeof(_IHotkeySystemEvents))]
|
|
[ProgId("DHS.HotkeySystem")]
|
|
public class HotkeySystemImpl : IHotkeySystem, IDecalService
|
|
{
|
|
private readonly List<RegisteredHotkey> _hotkeys = new List<RegisteredHotkey>();
|
|
private readonly Dictionary<object, IDHSHotkeyEvents> _connectedPlugins = new Dictionary<object, IDHSHotkeyEvents>();
|
|
|
|
// COM event — raised when a hotkey fires
|
|
public event _IHotkeySystemEvents_HotkeyEventEventHandler HotkeyEvent;
|
|
|
|
public void AddHotkey(string szPlugin, Hotkey pHotkey)
|
|
{
|
|
var impl = pHotkey as HotkeyImpl;
|
|
if (impl == null)
|
|
{
|
|
// Wrap foreign IHotkey implementation
|
|
impl = new HotkeyImpl
|
|
{
|
|
EventTitle = ((IHotkey)pHotkey).EventTitle,
|
|
EventDescription = ((IHotkey)pHotkey).EventDescription,
|
|
AltState = ((IHotkey)pHotkey).AltState,
|
|
ControlState = ((IHotkey)pHotkey).ControlState,
|
|
ShiftState = ((IHotkey)pHotkey).ShiftState,
|
|
VirtualKey = ((IHotkey)pHotkey).VirtualKey
|
|
};
|
|
}
|
|
|
|
_hotkeys.Add(new RegisteredHotkey
|
|
{
|
|
PluginCookie = szPlugin,
|
|
Hotkey = impl
|
|
});
|
|
}
|
|
|
|
public bool QueryHotkey(string bstrTitle)
|
|
{
|
|
foreach (var rh in _hotkeys)
|
|
{
|
|
if (string.Equals(rh.Hotkey.EventTitle, bstrTitle, StringComparison.OrdinalIgnoreCase))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public Hotkey QueryHotkeyEx(string szPluginCookie, string szTitle)
|
|
{
|
|
foreach (var rh in _hotkeys)
|
|
{
|
|
if (string.Equals(rh.PluginCookie, szPluginCookie, StringComparison.OrdinalIgnoreCase) &&
|
|
string.Equals(rh.Hotkey.EventTitle, szTitle, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return (Hotkey)(object)rh.Hotkey;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public void Connect(object pPlugin)
|
|
{
|
|
// Plugin implements IDHSHotkeyEvents for receiving hotkey notifications
|
|
var events = pPlugin as IDHSHotkeyEvents;
|
|
if (events != null)
|
|
{
|
|
_connectedPlugins[pPlugin] = events;
|
|
}
|
|
}
|
|
|
|
public void DeleteHotkey(string szPluginCookie, string szHotkeyName)
|
|
{
|
|
_hotkeys.RemoveAll(rh =>
|
|
string.Equals(rh.PluginCookie, szPluginCookie, StringComparison.OrdinalIgnoreCase) &&
|
|
string.Equals(rh.Hotkey.EventTitle, szHotkeyName, StringComparison.OrdinalIgnoreCase));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called by the input system when a key combination matches a registered hotkey.
|
|
/// Fires the HotkeyEvent and notifies connected plugins.
|
|
/// </summary>
|
|
internal bool FireHotkey(string eventTitle)
|
|
{
|
|
bool eat = false;
|
|
|
|
// Fire COM connection point event
|
|
HotkeyEvent?.Invoke(eventTitle, ref eat);
|
|
if (eat) return true;
|
|
|
|
// Notify connected plugins via IDHSHotkeyEvents
|
|
foreach (var plugin in _connectedPlugins.Values)
|
|
{
|
|
try
|
|
{
|
|
if (plugin.OnHotkey(eventTitle))
|
|
return true;
|
|
}
|
|
catch
|
|
{
|
|
// Plugin error — don't propagate
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check if a key press matches any registered hotkey and fire it.
|
|
/// </summary>
|
|
internal bool ProcessKey(int virtualKey, bool alt, bool ctrl, bool shift)
|
|
{
|
|
foreach (var rh in _hotkeys)
|
|
{
|
|
var hk = rh.Hotkey;
|
|
if (hk.VirtualKey == virtualKey &&
|
|
hk.AltState == alt &&
|
|
hk.ControlState == ctrl &&
|
|
hk.ShiftState == shift)
|
|
{
|
|
if (FireHotkey(hk.EventTitle))
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// IDecalService implementation
|
|
public void Initialize(DecalCore pDecal)
|
|
{
|
|
// DHS doesn't need DecalCore reference directly
|
|
}
|
|
|
|
public void BeforePlugins()
|
|
{
|
|
}
|
|
|
|
public void AfterPlugins()
|
|
{
|
|
}
|
|
|
|
public void Terminate()
|
|
{
|
|
_hotkeys.Clear();
|
|
_connectedPlugins.Clear();
|
|
}
|
|
}
|
|
}
|