/// <summary> /// Returns true if window matches. /// </summary> /// <param name="thc">This func uses the window handle (gets on demand) and WFCache.</param> internal bool Match(TriggerHookContext thc) { bool yes = false; var w = thc.Window; if (!w.Is0) { switch (o) { case AWnd.Finder f: yes = f.IsMatch(w, thc); break; case AWnd.Finder[] a: foreach (var v in a) { if (yes = v.IsMatch(w, thc)) { break; } } break; } } return(yes ^ not); }
internal bool MatchScopeWindowAndFunc(TriggerHookContext thc) { try { for (int i = 0; i < 3; i++) { if (i == 1) { if (scope != null) { thc.PerfStart(); bool ok = scope.Match(thc); thc.PerfEnd(false, ref scope.perfTime); if (!ok) { return(false); } } } else { var af = i == 0 ? _funcBefore : _funcAfter; if (af != null) { foreach (var v in af) { thc.PerfStart(); bool ok = v.f(thc.args); thc.PerfEnd(true, ref v.perfTime); if (!ok) { return(false); } } } } } } catch (Exception ex) { AOutput.Write(ex); return(false); } return(true); //never mind: when same scope used several times (probably with different functions), // should compare it once, and don't call 'before' functions again if did not match. Rare. }
internal void HookProcEdgeMove(EdgeMoveDetector_.Result d, TriggerHookContext thc) //d not in { ESubtype subtype; byte data, dataAnyPart; if (d.edgeEvent != 0) { subtype = ESubtype.Edge; data = (byte)d.edgeEvent; dataAnyPart = (byte)d.edgeEventAnyPart; } else { subtype = ESubtype.Move; data = (byte)d.moveEvent; dataAnyPart = (byte)d.moveEventAnyPart; } _HookProc2(thc, true, subtype, HookData.MouseEvent.Move, d.pt, data, dataAnyPart); }
/// <summary> /// Makes triggers alive. /// </summary> /// <remarks> /// This function monitors hotkeys, activated windows and other events. When an event matches an added trigger, launches the thrigger's action, which runs in other thread by default. /// Does not return immediately. Runs until this process is terminated or <see cref="Stop"/> called. /// </remarks> /// <example>See <see cref="ActionTriggers"/>.</example> /// <exception cref="InvalidOperationException">Already running.</exception> /// <exception cref="AuException">Something failed.</exception> public unsafe void Run() { //ADebug.PrintLoadedAssemblies(true, true, true); ThrowIfRunning_(); //bool haveTriggers = false; HooksThread.UsedEvents hookEvents = 0; _windowTriggers = null; for (int i = 0; i < _t.Length; i++) { var t = _t[i]; if (t == null || !t.HasTriggers) { continue; } //haveTriggers = true; switch ((TriggerType)i) { case TriggerType.Hotkey: hookEvents |= HooksThread.UsedEvents.Keyboard; break; case TriggerType.Autotext: hookEvents |= HooksThread.UsedEvents.Keyboard | HooksThread.UsedEvents.Mouse; break; case TriggerType.Mouse: hookEvents |= (t as MouseTriggers).UsedHookEvents_; break; case TriggerType.Window: _windowTriggers = t as WindowTriggers; break; } } //AOutput.Write(haveTriggers, (uint)llHooks); //if(!haveTriggers) return; //no. The message loop may be used for toolbars etc. if (!s_wasRun) { s_wasRun = true; AWnd.More.RegisterWindowClass(c_cn); } _wMsg = AWnd.More.CreateMessageOnlyWindow(_WndProc, c_cn); _mainThreadId = AThread.NativeId; _winTimerPeriod = 0; _winTimerLastTime = 0; if (hookEvents != 0) { //prevent big delay later on first LL hook event while hook proc waits if (!s_wasKM) { s_wasKM = true; ThreadPool.QueueUserWorkItem(_ => { try { //using var p1 = APerf.Create(); new AWnd.Finder("*a").IsMatch(AWnd.GetWnd.Root); //if used window scopes etc _ = AHookWin.LowLevelHooksTimeout; //slow JIT of registry functions Util.AJit.Compile(typeof(ActionTriggers), nameof(_WndProc), nameof(_KeyMouseEvent)); Util.AJit.Compile(typeof(TriggerHookContext), nameof(TriggerHookContext.InitContext), nameof(TriggerHookContext.PerfEnd), nameof(TriggerHookContext.PerfWarn)); Util.AJit.Compile(typeof(ActionTrigger), nameof(ActionTrigger.MatchScopeWindowAndFunc)); Util.AJit.Compile(typeof(HotkeyTriggers), nameof(HotkeyTriggers.HookProc)); AutotextTriggers.JitCompile(); MouseTriggers.JitCompile(); } catch (Exception ex) { ADebug.Print(ex); } }); } _thc = new TriggerHookContext(this); _ht = new HooksThread(hookEvents, _wMsg); } try { _evStop = Api.CreateEvent(false); _StartStopAll(true); IntPtr h = _evStop; _Wait(&h, 1); } finally { if (hookEvents != 0) { _ht.Dispose(); _ht = null; } Api.DestroyWindow(_wMsg); _wMsg = default; Stopping?.Invoke(this, EventArgs.Empty); _evStop.Dispose(); _StartStopAll(false); _mainThreadId = 0; _threads?.Dispose(); _threads = null; } void _StartStopAll(bool start) { foreach (var t in _t) { if (t?.HasTriggers ?? false) { t.StartStop(start); } } } }
bool _HookProc2(TriggerHookContext thc, bool isEdgeMove, ESubtype subtype, HookData.MouseEvent mEvent, POINT pt, byte data, byte dataAnyPart) { var mod = TrigUtil.GetModLR(out var modL, out var modR) | _eatMod; MouseTriggerArgs args = null; g1: if (_d.TryGetValue(_DictKey(subtype, data), out var v)) { if (!isEdgeMove) { thc.UseWndFromPoint(pt); } for (; v != null; v = v.next) { var x = v as MouseTrigger; if ((mod & x.modMask) != x.modMasked) { continue; } switch (x.flags & (TMFlags.LeftMod | TMFlags.RightMod)) { case TMFlags.LeftMod: if (modL != mod) { continue; } break; case TMFlags.RightMod: if (modR != mod) { continue; } break; } if (isEdgeMove && x.screenIndex != TMScreen.Any) { var screen = x.screenIndex == TMScreen.OfActiveWindow ? AScreen.Of(AWnd.Active) : AScreen.Index((int)x.screenIndex); if (!screen.Bounds.Contains(pt)) { continue; } } if (v.DisabledThisOrAll) { continue; } if (args == null) { thc.args = args = new MouseTriggerArgs(x, thc.Window, mod); //may need for scope callbacks too } else { args.Trigger = x; } if (!x.MatchScopeWindowAndFunc(thc)) { continue; } if (x.action == null) { _ResetUp(); } else if (0 != (x.flags & TMFlags.ButtonModUp) && (mod != 0 || subtype == ESubtype.Click)) { _upTrigger = x; _upArgs = args; _upEvent = subtype == ESubtype.Click ? mEvent : 0; _upMod = mod; } else { thc.trigger = x; _ResetUp(); } _eatMod = mod; if (mod != 0) { _SetTempKeybHook(); if (thc.trigger != null) { thc.muteMod = TriggerActionThreads.c_modRelease; } else { ThreadPool.QueueUserWorkItem(_ => AKeys.Internal_.ReleaseModAndDisableModMenu()); } } //AOutput.Write(mEvent, pt, mod); if (isEdgeMove || 0 != (x.flags & TMFlags.ShareEvent)) { return(false); } if (subtype == ESubtype.Click) { _eatUp = mEvent; } return(true); } } if (dataAnyPart != 0) { data = dataAnyPart; dataAnyPart = 0; goto g1; } return(false); }
internal bool HookProcClickWheel(HookData.Mouse k, TriggerHookContext thc) { //AOutput.Write(k.Event, k.pt); Debug.Assert(!k.IsInjectedByAu); //server must ignore ESubtype subtype; byte data; if (k.IsButton) { if (k.IsButtonUp) { if (k.Event == _upEvent) { _upEvent = 0; if (_upMod == 0 && _upTrigger != null) { thc.args = _upArgs; thc.trigger = _upTrigger; _ResetUp(); } } if (k.Event == _eatUp) { _eatUp = 0; return(true); //To be safer, could return false if AMouse.IsPressed(k.Button), but then can interfere with the trigger action. } return(false); //CONSIDER: _upTimeout. } if (k.Event == _eatUp) { _eatUp = 0; } subtype = ESubtype.Click; TMClick b; switch (k.Event) { case HookData.MouseEvent.LeftButton: b = TMClick.Left; break; case HookData.MouseEvent.RightButton: b = TMClick.Right; break; case HookData.MouseEvent.MiddleButton: b = TMClick.Middle; break; case HookData.MouseEvent.X1Button: b = TMClick.X1; break; default: b = TMClick.X2; break; } data = (byte)b; } else //wheel { subtype = ESubtype.Wheel; TMWheel b; switch (k.Event) { case HookData.MouseEvent.WheelForward: b = TMWheel.Forward; break; case HookData.MouseEvent.WheelBackward: b = TMWheel.Backward; break; case HookData.MouseEvent.WheelLeft: b = TMWheel.Left; break; default: b = TMWheel.Right; break; } data = (byte)b; } return(_HookProc2(thc, false, subtype, k.Event, k.pt, data, 0)); }
void _UpTriggered(TriggerHookContext thc) { thc.args = _upArgs; thc.trigger = _upTrigger; _UpClear(); }
internal bool HookProc(HookData.Keyboard k, TriggerHookContext thc) { //AOutput.Write(k.vkCode, !k.IsUp); Debug.Assert(!k.IsInjectedByAu); //server must ignore KKey key = k.vkCode; KMod mod = thc.Mod; bool up = k.IsUp; if (!up) { _UpClear(); } if (thc.ModThis != 0) { if (_upTrigger != null && mod == 0 && _upKey == 0) { _UpTriggered(thc); } } else if (up) { if (key == _upKey) { _upKey = 0; if (_upTrigger != null && mod == 0) { _UpTriggered(thc); } } if (key == _eatUp) { _eatUp = 0; return(true); //To be safer, could return false if AKeys.IsPressed(_eatUp), but then can interfere with the trigger action. } //CONSIDER: _upTimeout. } else { //if(key == _eatUp) _eatUp = 0; _eatUp = 0; if (_d.TryGetValue((int)key, out var v)) { HotkeyTriggerArgs args = null; for (; v != null; v = v.next) { var x = v as HotkeyTrigger; if ((mod & x.modMask) != x.modMasked) { continue; } switch (x.flags & (TKFlags.LeftMod | TKFlags.RightMod)) { case TKFlags.LeftMod: if (thc.ModL != mod) { continue; } break; case TKFlags.RightMod: if (thc.ModR != mod) { continue; } break; } if (v.DisabledThisOrAll) { continue; } if (args == null) { thc.args = args = new HotkeyTriggerArgs(x, thc.Window, key, mod); //may need for scope callbacks too } else { args.Trigger = x; } if (!x.MatchScopeWindowAndFunc(thc)) { continue; } if (x.action != null) { if (0 != (x.flags & TKFlags.KeyModUp)) { _upTrigger = x; _upArgs = args; _upKey = key; } else { thc.trigger = x; } } //AOutput.Write(key, mod); if (0 != (x.flags & TKFlags.ShareEvent)) { return(false); } if (thc.trigger == null) //KeyModUp or action==null { if (mod == KMod.Alt || mod == KMod.Win || mod == (KMod.Alt | KMod.Win)) { //AOutput.Write("need Ctrl"); ThreadPool.QueueUserWorkItem(o => AKeys.Internal_.SendKey(KKey.Ctrl)); //disable Alt/Win menu } } else if (mod != 0) { if (0 == (x.flags & TKFlags.NoModOff)) { thc.muteMod = TriggerActionThreads.c_modRelease; } else if (mod == KMod.Alt || mod == KMod.Win || mod == (KMod.Alt | KMod.Win)) { thc.muteMod = TriggerActionThreads.c_modCtrl; } } _eatUp = key; return(true); } } } return(false); }