#include %A_LineFile%\..\CLR.ahk class AutoHotInterception { _contextManagers := Map() __New() { bitness := A_PtrSize == 8 ? "x64" : "x86" dllName := "interception.dll" if (A_IsCompiled) { dllFile := A_LineFile "\..\Lib\" bitness "\" dllName DirCreate("Lib") FileInstall("Lib\AutoHotInterception.dll", "Lib\AutoHotInterception.dll", 1) if (bitness == "x86") { DirCreate("Lib\x86") FileInstall("Lib\x86\interception.dll", "Lib\x86\interception.dll", 1) } else { DirCreate("Lib\x64") FileInstall("Lib\x64\interception.dll", "Lib\x64\interception.dll", 1) } } else { dllFile := A_LineFile "\..\" bitness "\" dllName } if (!FileExist(dllFile)) { MsgBox("Unable to find " dllFile ", exiting...`nYou should extract both x86 and x64 folders from the library folder in interception.zip into AHI's lib folder.") ExitApp } hModule := DllCall("LoadLibrary", "Str", dllFile, "Ptr") if (hModule == 0) { this_bitness := A_PtrSize == 8 ? "64-bit" : "32-bit" other_bitness := A_PtrSize == 4 ? "64-bit" : "32-bit" MsgBox("Bitness of " dllName " does not match bitness of AHK.`nAHK is " this_bitness ", but " dllName " is " other_bitness ".") ExitApp } DllCall("FreeLibrary", "Ptr", hModule) dllName := "AutoHotInterception.dll" if (A_IsCompiled) { dllFile := A_LineFile "\..\Lib\" dllName } else { dllFile := A_LineFile "\..\" dllName } hintMessage := "Try right-clicking " dllFile ", select Properties, and if there is an 'Unblock' checkbox, tick it`nAlternatively, running Unblocker.ps1 in the lib folder (ideally as admin) can do this for you." if (!FileExist(dllFile)) { MsgBox("Unable to find " dllFile ", exiting...") ExitApp } asm := CLR_LoadLibrary(dllFile) try { this.Instance := asm.CreateInstance("AutoHotInterception.Manager") } catch { MsgBox(dllName " failed to load`n`n" hintMessage) ExitApp } if (this.Instance.OkCheck() != "OK") { MsgBox(dllName " loaded but check failed!`n`n" hintMessage) ExitApp } } GetInstance() { return this.Instance } ; --------------- Input Synthesis ---------------- SendKeyEvent(id, code, state) { this.Instance.SendKeyEvent(id, code, state) } SendMouseButtonEvent(id, btn, state) { this.Instance.SendMouseButtonEvent(id, btn, state) } SendMouseButtonEventAbsolute(id, btn, state, x, y) { this.Instance.SendMouseButtonEventAbsolute(id, btn, state, x, y) } SendMouseMove(id, x, y) { this.Instance.SendMouseMove(id, x, y) } SendMouseMoveRelative(id, x, y) { this.Instance.SendMouseMoveRelative(id, x, y) } SendMouseMoveAbsolute(id, x, y) { this.Instance.SendMouseMoveAbsolute(id, x, y) } SetState(state) { this.Instance.SetState(state) } MoveCursor(x, y, cm := "Screen", mouseId := -1) { if (mouseId == -1) mouseId := 11 ; Use 1st found mouse oldMode := A_CoordModeMouse CoordMode("Mouse", cm) Loop { MouseGetPos(&cx, &cy) dx := this.GetDirection(cx, x) dy := this.GetDirection(cy, y) if (dx == 0 && dy == 0) break this.SendMouseMove(mouseId, dx, dy) } CoordMode("Mouse", oldMode) } GetDirection(cp, dp) { d := dp - cp if (d > 0) return 1 if (d < 0) return -1 return 0 } ; --------------- Querying ------------------------ GetDeviceId(IsMouse, VID, PID, instance := 1) { static devType := Map(0, "Keyboard", 1, "Mouse") dev := this.Instance.GetDeviceId(IsMouse, VID, PID, instance) if (dev == 0) { MsgBox("Could not get " devType[isMouse] " with VID " VID ", PID " PID ", Instance " instance) ExitApp } return dev } GetDeviceIdFromHandle(isMouse, handle, instance := 1) { static devType := Map(0, "Keyboard", 1, "Mouse") dev := this.Instance.GetDeviceIdFromHandle(IsMouse, handle, instance) if (dev == 0) { MsgBox("Could not get " devType[isMouse] " with Handle " handle ", Instance " instance) ExitApp } return dev } GetKeyboardId(VID, PID, instance := 1) { return this.GetDeviceId(false, VID, PID, instance) } GetMouseId(VID, PID, instance := 1) { return this.GetDeviceId(true, VID, PID, instance) } GetKeyboardIdFromHandle(handle, instance := 1) { return this.GetDeviceIdFromHandle(false, handle, instance) } GetMouseIdFromHandle(handle, instance := 1) { return this.GetDeviceIdFromHandle(true, handle, instance) } GetDeviceList() { DeviceList := Map() arr := this.Instance.GetDeviceList() for v in arr { ; ToDo: Return a class, so code completion works? DeviceList[v.id] := { ID: v.id, VID: v.vid, PID: v.pid, IsMouse: v.IsMouse, Handle: v.Handle } } return DeviceList } ; ---------------------- Subscription Mode ---------------------- SubscribeKey(id, code, block, callback, concurrent := false) { this.Instance.SubscribeKey(id, code, block, callback, concurrent) } UnsubscribeKey(id, code) { this.Instance.UnsubscribeKey(id, code) } SubscribeKeyboard(id, block, callback, concurrent := false) { this.Instance.SubscribeKeyboard(id, block, callback, concurrent) } UnsubscribeKeyboard(id) { this.Instance.UnsubscribeKeyboard(id) } SubscribeMouseButton(id, btn, block, callback, concurrent := false) { this.Instance.SubscribeMouseButton(id, btn, block, callback, concurrent) } UnsubscribeMouseButton(id, btn) { this.Instance.UnsubscribeMouseButton(id, btn) } SubscribeMouseButtons(id, block, callback, concurrent := false) { this.Instance.SubscribeMouseButtons(id, block, callback, concurrent) } UnsubscribeMouseButtons(id) { this.Instance.UnsubscribeMouseButtons(id) } SubscribeMouseMove(id, block, callback, concurrent := false) { this.Instance.SubscribeMouseMove(id, block, callback, concurrent) } UnsubscribeMouseMove(id) { this.Instance.UnsubscribeMouseMove(id) } SubscribeMouseMoveRelative(id, block, callback, concurrent := false) { this.Instance.SubscribeMouseMoveRelative(id, block, callback, concurrent) } UnsubscribeMouseMoveRelative(id) { this.Instance.UnsubscribeMouseMoveRelative(id) } SubscribeMouseMoveAbsolute(id, block, callback, concurrent := false) { this.Instance.SubscribeMouseMoveAbsolute(id, block, callback, concurrent) } UnsubscribeMouseMoveAbsolute(id) { this.Instance.UnsubscribeMouseMoveAbsolute(id) } ; ------------- Context Mode ---------------- ; Creates a context class to make it easy to turn on/off the hotkeys CreateContextManager(id) { if (this._contextManagers.Has(id)) { Msgbox("ID " id " already has a Context Manager") ExitApp } cm := AutoHotInterception.ContextManager(this, id) this._contextManagers[id] := cm return cm } RemoveContextManager(id) { if (!this._contextManagers.Has(id)) { Msgbox("ID " id " does not have a Context Manager") ExitApp } this._contextManagers[id].Remove() this._contextManagers.Delete(id) } ; Helper class for dealing with context mode class ContextManager { IsActive := 0 __New(parent, id) { this.parent := parent this.id := id result := this.parent.Instance.SetContextCallback(id, this.OnContextCallback.Bind(this)) } OnContextCallback(state) { Sleep 0 this.IsActive := state } Remove() { this.parent.Instance.RemoveContextCallback(this.id) } } }