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 _hotkeys = new List(); private readonly Dictionary _connectedPlugins = new Dictionary(); // 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)); } /// /// Called by the input system when a key combination matches a registered hotkey. /// Fires the HotkeyEvent and notifies connected plugins. /// 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; } /// /// Check if a key press matches any registered hotkey and fire it. /// 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(); } } }