ahk-joystick-focus/focus.ahk
alterNERDtive 59d63e42b0
Now watches for process instead of window
Deciding whether or not a tool is already up by a window existing fails
if the tool is started, but hasn’t created a window yet. This is not an
issue if watching for the _process_ to pop up.

Ideally this would also close processes instead of windows when the
watched game goes down, but killing a process from AHK sadly is
ungraceful, unlike closing a window.
2022-09-09 18:31:09 +02:00

187 lines
5 KiB
AutoHotkey
Executable file
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; This script will raise / focus a target application when you move
; your Joystick/Throttle/Gamepad/whatever axes or hit a button.
;
; Im using it with twin sticks for Elite Dangerous, but it should be
; generically usable.
;
; It will also start a list of tools if the target application is
; detected and (optional, default on) close them again when the
; target is gone.
;
; Make sure your focus.ini file is setup properly. If not you might
; have to go kill the script from the systray or Task Manager :)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
#NoEnv
sendMode Input
setWorkingDir %A_ScriptDir%
#SingleInstance force
#MaxThreadsPerHotkey 1
#InstallKeybdHook
#Persistent
; check for config file and exit if it doesnt exist
if (!FileExist("focus.ini")) {
msgbox, No config file found. Please follow the instructions in the README file.
ExitApp, 1
}
; read focus.ini
config := readConfig("focus.ini")
state := {}
; set the target watcher
SetTimer, watchTarget, % config["polling"]["targetcheckrate"]
; reads the config file
readConfig(file) {
config := []
; [target]
IniRead, tname, % file, target, name
config["target","name"] := tname
; [polling]
IniRead, prate, % file, polling, pollingrate
IniRead, tcrate, % file, polling, targetcheckrate
config["polling"] := {"pollingrate": prate, "targetcheckrate": tcrate}
; [devices]
IniRead, thold, % file, devices, threshold
names := []
error := False
while (!error) {
IniRead, tmp, % file, devices, device%A_Index%
if (tmp == "ERROR")
error := True
else
names.push(tmp)
}
devcount := names.MaxIndex()
senses := []
useAxes := []
useButtons := []
loop, %devcount%
{
IniRead, tmp, % file, devices, device%A_Index%sensitivity
if (tmp == "ERROR")
tmp := 1
senses.push(tmp)
IniRead, tmp, % file, devices, device%A_Index%useAxes
if (tmp == "ERROR")
tmp := True
else
tmp := %tmp%
useAxes.push(tmp)
IniRead, tmp, % file, devices, device%A_Index%useButtons
if (tmp == "ERROR")
tmp := True
else
tmp := %tmp%
useButtons.push(tmp)
}
config["devices"] := {"threshold": thold, "names": names, "sensitivities": senses, "useAxes": useAxes, "useButtons": useButtons}
; [tools]
IniRead, tkill, % file, tools, kill
paths := []
error := False
while (!error) {
IniRead, tmp, % file, tools, tool%A_Index%
if (tmp == "ERROR")
error := True
else
paths.push(tmp)
}
config["tools"] := {"kill": %tkill%, "paths": paths}
return config
}
; nothing but jump labels beyond this point, lets make it explicit
; (AHK v2 will finally get rid of those …)
return
; this one will check for a running target process
; if found
; 1. start the actual stick watching routine at a higher polling frequency
; 2. start ALL THE TOOLS
; if not found
; 1. stop the stick watching routine
; 2. (optional) stop ALL THE TOOLS
watchTarget:
watchTarget()
return
watchTarget() {
global config
if (WinExist("ahk_exe " config["target"]["name"])) {
; start stick watcher
SetTimer, watchSticks, % config["polling"]["pollingrate"]
; start tools
for ip, path in config["tools"]["paths"] {
SplitPath, path, file
Process, Exist, %file%
if (!ErrorLevel) {
Run, % path
}
}
}
else {
; stop stick watcher
SetTimer, watchSticks, Delete
; stop tools
if (config["tools"]["kill"]) {
for ip, path in config["tools"]["paths"] {
SplitPath, path, file
if (WinExist("ahk_exe " file)) {
WinClose
}
}
}
}
}
; this one will check the sticks for input on the X/Y axes or buttons
; if an axis is currently active or a button down, focuses the target program
watchSticks:
watchSticks()
return
watchSticks() {
global config, state
target := "ahk_exe " config["target"]["name"]
; poll all axes
for id, dev in config["devices"]["names"] {
; check if axes are enabled for this device
if (config["devices"]["useAxes"][id]) {
for ia, axis in [ "X", "Y", "Z", "R", "U", "V" ] {
if (abs(getKeyState(dev axis) - state[id][axis])
* config["devices"]["sensitivities"][id]
> config["devices"]["threshold"]) {
; focus the target!
WinActivate, % target
}
state[id, axis] := getKeyState(dev axis)
}
}
}
; check ALL THE BUTTONS!
for id, dev in config["devices"]["names"] {
; check if buttons are enabled for this device
if (config["devices"]["useButtons"][id]) {
; get button count for the device and loop over all of them
buttons := getKeystate(dev "Buttons")
Loop, %buttons%
{
if (getKeyState(dev A_Index)) {
; focus the target!
WinActivate, % target
}
}
}
}
}