openDecal/Managed/Decal.DecalInput/InputServiceImpl.cs
erik d1442e3747 Initial commit: Complete open-source Decal rebuild
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>
2026-02-08 18:27:56 +01:00

170 lines
5.2 KiB
C#

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Decal.Interop.Core;
using Decal.Interop.Input;
namespace Decal.DecalInput
{
[ComVisible(true)]
[Guid("B33307BA-706D-474A-80B9-70BB8D13EF3E")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("DecalInput.InputService")]
public class InputServiceImpl : IInputService, IDecalService
{
internal static InputServiceImpl Instance { get; private set; }
private IDecalCore _decalCore;
private readonly List<HotkeyImpl> _hotkeys = new List<HotkeyImpl>();
private readonly List<TimerImpl> _timers = new List<TimerImpl>();
private readonly List<WinMsgHookImpl> _msgHooks = new List<WinMsgHookImpl>();
private readonly Dictionary<string, int> _commands = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
private WndMsgImpl _message;
internal InputBufferImpl ActiveBuffer { get; set; }
public DecalCore Decal => (DecalCore)_decalCore;
// Parameterized property - KeyByName takes a string parameter via IDispatch
public int KeyByName => 0;
internal int GetKeyByName(string name)
{
return KeyNames.VKFromName(name);
}
// IDecalService
public void Initialize(DecalCore pDecal)
{
_decalCore = (IDecalCore)pDecal;
Instance = this;
_message = new WndMsgImpl();
}
public void BeforePlugins()
{
// In native code: subclasses game window and loads keymap
// Window hooking requires being in-process, handled by Inject.DLL
}
public void AfterPlugins()
{
_commands.Clear();
// Native code restores original window procedure here
}
public void Terminate()
{
_hotkeys.Clear();
_timers.Clear();
_msgHooks.Clear();
_commands.Clear();
_message = null;
ActiveBuffer = null;
if (Instance == this)
Instance = null;
}
// Registration methods for child objects
internal void RegisterHotkey(HotkeyImpl hotkey)
{
if (!_hotkeys.Contains(hotkey))
_hotkeys.Add(hotkey);
}
internal void UnregisterHotkey(HotkeyImpl hotkey)
{
_hotkeys.Remove(hotkey);
}
internal void RegisterTimer(TimerImpl timer)
{
if (!_timers.Contains(timer))
_timers.Add(timer);
}
internal void UnregisterTimer(TimerImpl timer)
{
_timers.Remove(timer);
}
internal void RegisterMsgHook(WinMsgHookImpl hook)
{
if (!_msgHooks.Contains(hook))
_msgHooks.Add(hook);
}
internal void UnregisterMsgHook(WinMsgHookImpl hook)
{
_msgHooks.Remove(hook);
}
/// <summary>
/// Called from the window hook procedure for each window message.
/// Returns true if the message should be eaten (suppressed).
/// </summary>
internal bool ProcessWindowMessage(int hwnd, int msg, int wParam, int lParam)
{
_message.Set(hwnd, msg, wParam, lParam);
// Process message hooks
foreach (var hook in _msgHooks)
{
hook.FireMessage(hook, _message);
if (_message.Eat)
return true;
}
// Process hotkeys on WM_KEYUP (0x0101), only if no active buffer
if (msg == 0x0101 && ActiveBuffer == null)
{
foreach (var hotkey in _hotkeys)
{
if (hotkey.Enabled && hotkey.Key.Length > 0)
{
// Match virtual key
int vk = wParam;
string keyName = hotkey.Key;
int hotkeyVk;
if (keyName.StartsWith("{") && keyName.EndsWith("}"))
{
string name = keyName.Substring(1, keyName.Length - 2);
hotkeyVk = KeyNames.VKFromName(name);
}
else
{
hotkeyVk = char.ToUpper(keyName[0]);
}
if (vk == hotkeyVk)
{
hotkey.FireHotkey();
break; // Only one hotkey per key press
}
}
}
}
return false;
}
/// <summary>
/// Called from the render loop to process timers and input buffer delays.
/// </summary>
internal void ProcessTimers()
{
uint now = GetTickCount();
// Copy list to avoid modification during iteration
var timersCopy = new List<TimerImpl>(_timers);
foreach (var timer in timersCopy)
{
timer.CheckTimeout(now);
}
}
[DllImport("kernel32.dll")]
private static extern uint GetTickCount();
}
}