The reason being that AHK is not a multi-threaded language, and is not well suited to high-volume updates (Such as mouse positional info).
Bear in mind that AHI is interacting with a keyboard and mouse driver. If AHI locks up, all filtered devices lock up, so I need to try and at least take precautions to try and stop AHI locking up if a callback in someone's AHK script takes ages to process (eg due to a sleep), thus stalling the processing queue. Therefore, my code fires off each callback on a new thread, so your callback functions (especially eg the mouse one) will have the absolute hell bombarded out of them.
This becomes an issue, when, for example, you want to delay reaction to something (eg the trigger) - just doing a sleep when one gun pulls it's trigger means another bit of code can lock up.
From your code, it is fairly apparent that to get you to the point you need to be, with you attempting to write the code, would take a lot of back-and-forth, so I decided to write something for you. Now this code may well initially go somewhat over your head, but if you can wrap your head around it, this is a very neat way of doing things.
Something you got right though was the turning on and off blocking. I had never anticipated using AHI like that, but it seems quite happy
You have two challenges which you seem to be grappling with - scope (lots of variables which need to be used in various places) and duplicity (two guns with identical logic, and you need to process both).
The answer to both of these problems is classes.
A class allows you to "encapsulate" all the logic and data for a particular "thing" into one object.
So we write a "Gun" class that knows how to handle one gun, then create two of those classes and each functions identically.
We can then easily write the code and test it with one gun, then when we have it working, create another gun class and it all works.
Each gun class can hold it's own set of variables or "properties" (eg what interception ID it is, what it's flashkey is etc) and functions which operate upon those properties.
No need for loads of global variables - you don't need a `global flashkey1`, you just need a concept of "MY flashkey".
Code: Select all
#SingleInstance force
#Persistent
#include Lib\AutoHotInterception.ahk
InterceptionWrapper := new AutoHotInterception()
global Interception := InterceptionWrapper.GetInstance()
devices := InterceptionWrapper.GetDeviceList()
if (!devices.Length()){
msgbox Device List Check failed
ExitApp
}
;id1 := Interception.GetDeviceId(true, 0x0B9A, 0x016A)
;id2 := Interception.GetDeviceId(true, 0x0B9A, 0x016A, 2) ; Uses new "Return 2nd instance of this VID/PID syntax
id1 := 15
gun1 := new Gun(id1, "a")
;gun2 := new Gun(id2, "k")
return
^Esc::
ExitApp
; ==========================================================================
/*
LightGun class for AutoHotInterception
Usage:
myGun := new Gun(<id>, <flashkey>)
eg
myGun := new Gun(15, "{F12}")
id The Interception ID of the Gun
flashkey The AHK key name of the key to send for the flash
*/
Class Gun {
; Configure these to your liking
flashtime := 500 ; The amount of time the flash lasts
mousebutton := 1 ; Which mouse button is the trigger?
; Called when you create a new Gun
__New(id, flashkey){
this.id := id
this.flashkey := flashkey
; Create the function needed for the flash timer
this.FlashEndFn := this.FlashEnd.Bind(this)
; Subscribe to the fire button
Interception.SubscribeMouseButton(this.id, this.mousebutton, true, this.ButtonEvent.Bind(this))
; Subscribe to movement, turn block off
this.SetSubscriptionState(0)
}
; Called when the trigger is pressed or released
ButtonEvent(state){
if (state){
; Button was pressed
this.DoFlashSequence()
}
}
; Called when the mouse moves
MouseEvent(x, y){
;~ Tooltip % x
}
; Called when the flash seqence starts
DoFlashSequence(){
;~ ToolTip % "Interception ID " this.id " Flash Start @ " A_TickCount
this.SetSubscriptionState(1) ; block movement
Send % this.flashkey ; Send the flash key
this.SetButtonState(1) ; press the trigger
; Start the timer running to handle end of flash
fn := this.FlashEndFn
SetTimer, % fn, % "-" this.flashtime
}
; Called at the end of the flash, on a timer
FlashEnd(){
this.SetButtonState(0) ; release the trigger
this.SetSubscriptionState(0) ; unblock movement
;~ ToolTip % "Interception ID " this.id " Flash End @ " A_TickCount
}
; Set the state of the button
SetButtonState(state){
Interception.SendMouseButtonEvent(this.id, this.mousebutton, state)
}
; Set state of movement subscription (blocked / unblocked)
SetSubscriptionState(blocked){
Interception.SubscribeMouseMoveAbsolute(this.id, blocked, this.MouseEvent.Bind(this))
}
}