Multithreaded customizable RGB clicker
Posted: 19 Sep 2023, 11:51
Figured with all the time i put into this trying to figure it out and amount of code i probably snagged from others posts here figure i should post what i got done now
This will check X,Y position for a RGB color and if found trigger the hotkey. You can create multiple threads for different locations. Dunno if anyone else would find this useful,but here goes nothing
This will check X,Y position for a RGB color and if found trigger the hotkey. You can create multiple threads for different locations. Dunno if anyone else would find this useful,but here goes nothing
Code: Select all
; Copyright © 2023 Faren <farendil2020@gmail.com>
; This work is free. You can redistribute it and/or modify it under the
; terms of the Do What The Fuck You Want To Public License, Version 2,
; as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
#include <JSON>
;OPTIMIZATIONS START
#NoEnv
#MaxHotkeysPerInterval 99000000
#HotkeyInterval 99000000
#KeyHistory 0
#Warn
ListLines Off
Process, Priority, , A
SetBatchLines, -1
SetKeyDelay, -1, -1
SetMouseDelay, -1
SetDefaultMouseSpeed, 0
SetWinDelay, -1
SetControlDelay, -1
SendMode Input
SetWorkingDir %A_ScriptDir%
DetectHiddenWindows, on
;OPTIMIZATIONS END
CoordMode, Pixel, Screen
CoordMode, Mouse, Screen
pixelSearchMode = "RGB"
global splashFile := ".\recycle.png"
file := ".\actions.ini"
; sample file format listed at end of script
toggle := 0,
paused := 0,
global debug := 0
global error := 1
global actions := []
global actionsJSON
global numThreads := 0
Loop read, %file%
{
if SubStr(A_LoopReadLine,1,2)=="//" or SubStr(A_LoopReadLine,1,1)==";"
continue
subarray := StrSplit(A_LoopReadLine, "csv")
if SubStr(subarray,1,2)=="//"
continue
if SubStr(subarray,1,1)==";"
continue
actions.Insert(subarray)
}
actionsJSON = % JSON.Dump(actions)
global PID1:=0, pname1, execScript1,
PID2:=0, pname2, execScript2,
numThreads := 2
execScript1 := buildScript(1, 637, 521, 30, 225, 670, "Secondary")
execScript2 := buildScript(2, 637, 477, 45, 200, 690, "Main")
^!r::Reload ; Ctrl+Alt+R
;=============================================================================
; alt-` : One-Key Rotation (Single Target)
;#ifWinActive <Window Title>
`::
toggle := !toggle
if (toggle) {
Loop, %numThreads% {
DetectHiddenWindows, on
script := execScript%A_Index%
sname = script%A_Index%
PID := ExecScript(script,[actionsJSON],A_AhkPath).ProcessID
PID%A_Index% := PID
WinWait, ahk_pid %PID%
WinGetTitle, pname%A_Index%, ahk_pid %PID%
}
} else {
killCMD := "/c taskkill /F"
loop, %numThreads% {
pid = PID%A_Index%
killCMD .= " /PID "%pid%
}
killCMD .= " /T && exit"
run, %comspec% %killCMD%,, hide
}
ScriptStatus(toggle)
return
!`::
paused := !paused
loop, %numThreads% {
pname := pname%A_Index%
pauseSuspendScript(pname, paused, paused)
}
return
buildScript(script, X:=0, Y:=0, delay:=0, tipX:=0, tipY:=0, desc="") {
var1 =
( %
#include <JSON>
;OPTIMIZATIONS START
#NoTrayIcon
#NoEnv
#MaxHotkeysPerInterval 99000000
#HotkeyInterval 99000000
#KeyHistory 0
#Warn
ListLines Off
Process, Priority, , A
SetBatchLines, -1
SetKeyDelay, -1, -1
SetMouseDelay, -1
SetDefaultMouseSpeed, 0
SetWinDelay, -1
SetControlDelay, -1
SendMode Input
SetWorkingDir %A_ScriptDir%
DetectHiddenWindows, on
CoordMode, Pixel, Screen
CoordMode, Mouse, Screen
;OPTIMIZATIONS END
global pixelSearchMode = "RGB"
actionsJSON = %1% ; arg sent as a JSON version of actions array
global myactions := JSON.Load(actionsJSON)
loop {
)
var2 = checkPixel(%X%, %Y%, %script%, %tipX%, %tipY%, %delay%)
var3 =
( %
}
checkPixel(Byref X, Byref Y, Byref script, Byref tipX, Byref tipY, delay:=0) {
PixelGetColor, pColor, X, Y, %pixelSearchMode%
for _, vAction in myactions {
while (GetKeyState("LShift", "P")){
}
if ((vAction.5 = 0) or (vAction.5 = script)) {
if (pColor = vAction.2) {
; Can uncomment this to see whats being triggered in a tooltip
; tooltip % script ": " vAction.1 ": " vAction.4 , tipX, tipY
send % vAction.4
break
}
}
}
DllCall("Sleep","UInt",delay)
}
)
execScript := var1 "`n" var2 "`n" var3
return execScript
}
;=============================================================================
;
ScriptStatus(mode) {
if (mode) {
Splashimage, %splashFile%, B W50 H50 X750 Y1,,, ScriptStatus
} else {
SplashImage, Off
}
}
;=============================================================================
;
Log(text,newline) {
OutputDebug % "AHK| " text
if (newline)
OutputDebug % "`n"
}
;=============================================================================
;
StrSplit(InputVar, Delimiter=",", OmitChars=" `t") {
array := []
Loop Parse, InputVar, %Delimiter%, %OmitChars%
array.Insert(A_LoopField)
return array
}
;=============================================================================
; return difference in 2 rgb colors
ColorDiff(color1, color2, mode:=0) {
Format:=A_FormatInteger
SetFormat, Integer, hex
r1:=color1>>16, r2:=color2>>16
g1:=color1>>8&0xFF, g2:=color2>>8&0xFF
b1:=color1&0xFF, b2:=color2&0xFF
SetFormat,Integer, % Format
diffr:=Abs(r1 - r2), diffg:=Abs(g1 - g2), diffb:=Abs(b1 - b2)
if mode=1
return diffr+diffg+diffb
else return diffr>diffg&&diffr>diffb?diffr:diffg>diffb?diffg:diffb
}
;=============================================================================
; pauseSuspendScript(ScriptTitle, suspendHotkeys := False, pauseScript := False)
pauseSuspendScript(Byref ScriptTitle, suspendHotkeys := False, pauseScript := False) { ;-- function to suspend/pause another script
prevDetectWindows := A_DetectHiddenWindows
prevMatchMode := A_TitleMatchMode
DetectHiddenWindows, On
SetTitleMatchMode, 2
if (script_id := WinExist(ScriptTitle " ahk_class AutoHotkey")) {
; Force the script to update its Pause/Suspend checkmarks.
SendMessage, 0x211,,,, ahk_id %script_id% ; WM_ENTERMENULOOP
SendMessage, 0x212,,,, ahk_id %script_id% ; WM_EXITMENULOOP
; Get script status from its main menu.
mainMenu := DllCall("GetMenu", "uint", script_id)
fileMenu := DllCall("GetSubMenu", "uint", mainMenu, "int", 0)
isPaused := DllCall("GetMenuState", "uint", fileMenu, "uint", 4, "uint", 0x400) >> 3 & 1
isSuspended := DllCall("GetMenuState", "uint", fileMenu, "uint", 5, "uint", 0x400) >> 3 & 1
DllCall("CloseHandle", "uint", fileMenu)
DllCall("CloseHandle", "uint", mainMenu)
if (suspendHotkeys && !isSuspended) || (!suspendHotkeys && isSuspended)
PostMessage, 0x111, 65305, 1,, ahk_id %script_id% ; this toggles the current suspend state.
if (pauseScript && !isPaused) || (!pauseScript && isPaused)
PostMessage, 0x111, 65403,,, ahk_id %script_id% ; this toggles the current pause state.
}
DetectHiddenWindows, %prevDetectWindows%
SetTitleMatchMode, %prevMatchMode%
return script_id
}
/* ===========================================================================
* Function: ExecScript
* Run/execute AutoHotkey script[file, through named pipe(s) or from stdin]
* Mod of/inspired by HotKeyIt's DynaRun()
* License:
* WTFPL [http://wtfpl.net/]
* Syntax:
* exec := ExecScript( code [ , args, kwargs* ] )
* Parameter(s)/Return Value:
* exec [retval] - a WshScriptExec object [http://goo.gl/GlEzk5]
* if WshShell.Exec() method is used else 0 for
* WshShell.Run()
* script [in] - AHK script(file) or code(string) to run/execute.
* When running from stdin(*), if code contains
* unicode characters, WshShell will raise an
* exception.
* args [in, opt] - array of command line arguments to pass to the
* script. Quotes(") are automatically escaped(\").
* kwargs* [in, variadic] - string in the following format: 'option=value',
* where 'option' is one or more of the following
* listed in the next section.
* Options(kwargs* parameter):
* ahk - path to the AutoHotkey executable to use which is relative to
* A_WorkingDir if an absolute path isn't specified.
* name - when running through named pipes, 'name' specifies the pipe name.
* If omitted, a random value is generated. Otherwise, specify an
* asterisk(*) to run from stdin. This option is ignored when a file
* is specified for the 'script' parameter.
* dir - working directory which is assumed to be relative to A_WorkingDir
* if an absolute path isn't specified.
* cp - codepage [UTF-8, UTF-16, CPnnn], default is 'CP0'. 'CP' may be
* omitted when passing in 'CPnnn' format. Omit or use 'CP0' when
* running code from stdin(*).
* child - if 1(true), WshShell.Exec() method is used, otherwise .Run().
* Default is 1. Value is ignored and .Exec() is always used when
* running code from stdin.
* Example:
* exec := ExecScript("MsgBox", ["arg"], "name=some_name", "dir=C:\Users")
* Credits:
* - Lexikos for his demonstration [http://goo.gl/5IkP5R]
* - HotKeyIt for DynaRun() [http://goo.gl/92BBMr]
*/
ExecScript(Byref script, args:="", kwargs*) {
;// Set default values for options first
child := true ;// use WshShell.Exec(), otherwise .Run()
, name := "AHK_" . A_TickCount
, dir := ""
, ahk := A_AhkPath
, cp := 0
for i, kwarg in kwargs
if ( option := SubStr(kwarg, 1, (i := InStr(kwarg, "="))-1) )
; the RegEx check is not really needed but is done anyways to avoid
; accidental override of internal local var(s)
&& ( option ~= "i)^child|name|dir|ahk|cp$" )
%option% := SubStr(kwarg, i+1)
pipe := (run_file := FileExist(script)) || (name == "*") ? 0 : []
Loop % pipe ? 2 : 0
{
;// Create named pipe(s), throw exception on failure
if (( pipe[A_Index] := DllCall(
(Join, Q C
"CreateNamedPipe" ; http://goo.gl/3aJQg7
"Str", "\\.\pipe\" . name ; lpName
"UInt", 2 ; dwOpenMode = PIPE_ACCESS_OUTBOUND
"UInt", 0 ; dwPipeMode = PIPE_TYPE_BYTE
"UInt", 255 ; nMaxInstances
"UInt", 0 ; nOutBufferSize
"UInt", 0 ; nInBufferSize
"Ptr", 0 ; nDefaultTimeOut
"Ptr", 0 ; lpSecurityAttributes
)) ) == -1) ; INVALID_HANDLE_VALUE
throw Exception("ExecScript() - Failed to create named pipe", -1, A_LastError)
}
; Command = {ahk_exe} /ErrorStdOut /CP{codepage} {file}
static fso := ComObjCreate("Scripting.FileSystemObject")
static q := Chr(34) ;// quotes("), for v1.1 and v2.0-a compatibility
cmd := Format("{4}{1}{4} /ErrorStdOut /CP{2} {4}{3}{4}"
, fso.GetAbsolutePathName(ahk)
, cp="UTF-8" ? 65001 : cp="UTF-16" ? 1200 : cp := Round(LTrim(cp, "CPcp"))
, pipe ? "\\.\pipe\" . name : run_file ? script : "*", q)
; Process and append parameters to pass to the script
for each, arg in args
{
i := 0
while (i := InStr(arg, q,, i+1)) ;// escape '"' with '\'
if (SubStr(arg, i-1, 1) != "\")
arg := SubStr(arg, 1, i-1) . "\" . SubStr(arg, i++)
cmd .= " " . (InStr(arg, " ") ? q . arg . q : arg)
}
if cwd := (dir != "" ? A_WorkingDir : "") ;// change working directory if needed
SetWorkingDir %dir%
static WshShell := ComObjCreate("WScript.Shell")
exec := (child || name == "*") ? WshShell.Exec(cmd) : WshShell.Run(cmd)
if cwd ;// restore working directory if altered above
SetWorkingDir %cwd%
if !pipe ;// file or stdin(*)
{
if !run_file ;// run stdin
exec.StdIn.WriteLine(script), exec.StdIn.Close()
return exec
}
DllCall("ConnectNamedPipe", "Ptr", pipe[1], "Ptr", 0) ;// http://goo.gl/pwTnxj
DllCall("CloseHandle", "Ptr", pipe[1])
DllCall("ConnectNamedPipe", "Ptr", pipe[2], "Ptr", 0)
if !(f := FileOpen(pipe[2], "h", cp))
return A_LastError
f.Write(script) ;// write dynamic code into pipe
f.Close(), DllCall("CloseHandle", "Ptr", pipe[2]) ;// close pipe
return exec
}
;=============================================================================
; Sample actions.ini file with 80 possible trigger colors listed
;
; var is to check for a variance in the color to trigger
; hotkey is the triggered hotkey
; zone will only let that hotkey trigger if it's in the X,Y "zone" or script # listed. 0 ignores
; should just need to modify RGB values and hotkey and a name if you want, rest can be left as is
;
;Name(color), RGB hex, var, hotkey, zone
;========================================================================
;action1(black), 0x000000, 0, !1, 0
;action2(dark gray 4), 0x434343, 0, +2, 0
;action3(dark gray 3), 0x666666, 0, 3, 0
;action4(dark gray 2), 0x999999, 0, 4, 0
;action5(dark gray 1), 0xB7B7B7, 0, 5, 0
;action6(gray), 0xCCCCCC, 0, 6, 0
;action7(light gray 1), 0xD9D9D9, 0, 7, 0
;action8(light gray 2), 0xEFEFEF, 0, 8, 0
;action9(light gray 3), 0xF3F3F3, 0, 9, 0
;action10(white), 0xFFFFFF, 0, 10, 0
;action11(red berry), 0x980000, 0, 11, 0
;action12(red), 0xFF0000, 0, 12, 0
;action13(orange), 0xFF9900, 0, 13, 0
;action14(yellow), 0xFFFF00, 0, 14, 0
;action15(green), 0x00FF00, 0, 15, 0
;action16(cyan), 0x00FFFF, 0, 16, 0
;action17(cornflower blue), 0x4A86E8, 0, 17, 0
;action18(blue), 0x0000FF, 0, 18, 0
;action19(purple), 0x9900FF, 0, 19, 0
;action20(magenta), 0xFF00FF, 0, 20, 0
;action21(light red berry 3), 0xE6B8AF, 0, 21, 0
;action22(light red 3), 0xF4CCCC, 0, 22, 0
;action23(light orange 3), 0xFCE5CD, 0, 23, 0
;action24(light yellow 3), 0xFFF2CC, 0, 24, 0
;action25(light green 3), 0xD9EAD3, 0, 25, 0
;action26(light cyan 3), 0xD0E0E3, 0, 26, 0
;action27(light cornflower blue 3), 0xC9DAF8, 0, 27, 0
;action28(light blue 3), 0xCFE2F3, 0, 28, 0
;action29(light purple 3), 0xD9D2E9, 0, 29, 0
;action30(light magenta 3), 0xEAD1DC, 0, 30, 0
;action31(light red berry 2), 0xDD7E6B, 0, 31, 0
;action32(light red 2), 0xEA9999, 0, 32, 0
;action33(light orange 2), 0xF9CB9C, 0, 33, 0
;action34(light yellow 2), 0xFFE599, 0, 34, 0
;action35(light green 2), 0xB6D7A8, 0, 35, 0
;action36(light cyan 2), 0xA2C4C9, 0, 36, 0
;action37(light cornflower blue 2), 0xA4C2F4, 0, 37, 0
;action38(light blue 2), 0x9FC5E8, 0, 38, 0
;action39(light purple 2), 0xB4A7D6, 0, 39, 0
;action40(light magenta 2), 0xD5A6BD, 0, 40, 0
;action41(light red berry 1), 0xCC4125, 0, 41, 0
;action42(light red 1), 0xE06666, 0, 42, 0
;action43(light orange 1), 0xF6B26B, 0, 43, 0
;action44(light yellow 1), 0xFFD966, 0, 44, 0
;action45(light green 1), 0x93C47D, 0, 45, 0
;action46(light cyan 1), 0x76A5AF, 0, 46, 0
;action47(light cornflower blue 1), 0x6D9EEB, 0, 47, 0
;action48(light blue 1), 0x6FA8DC, 0, 48, 0
;action49(light purple 1), 0x8E7CC3, 0, 49, 0
;action50(light magenta 1), 0xC27BA0, 0, 50, 0
;action51(dark red berry 1), 0xA61C00, 0, 51, 0
;action52(dark red 1), 0xCC0000, 0, 52, 0
;action53(dark orange 1), 0xE69138, 0, 53, 0
;action54(dark yellow 1), 0xF1C232, 0, 54, 0
;action55(dark green 1), 0x6AA84F, 0, 55, 0
;action56(dark cyan 1), 0x45818E, 0, 56, 0
;action57(dark cornflower blue 1), 0x3C78D8, 0, 57, 0
;action58(dark blue 1), 0x3D85C6, 0, 58, 0
;action59(dark purple 1), 0x674EA7, 0, 59, 0
;action60(dark magenta 1), 0xA64D79, 0, 60, 0
;action61(dark red berry 2), 0x85200C, 0, 61, 0
;action62(dark red 2), 0x990000, 0, 62, 0
;action63(dark orange 2), 0xB45F06, 0, 63, 0
;action64(dark yellow 2), 0xBF9000, 0, 64, 0
;action65(dark green 2), 0x38761D, 0, 65, 0
;action66(dark cyan 2), 0x134F5C, 0, 66, 0
;action67(dark cornflower blue 2), 0x1155CC, 0, 67, 0
;action68(dark blue 2), 0x0B5394, 0, 68, 0
;action69(dark purple 2), 0x351C75, 0, 69, 0
;action70(dark magenta 2), 0x741B47, 0, 70, 0
;action71(dark red berry 3), 0x5B0F00, 0, 71, 0
;action72(dark red 3), 0x660000, 0, 72, 0
;action73(dark orange 3), 0x783F04, 0, 73, 0
;action74(dark yellow 3), 0x7F6000, 0, 74, 0
;action75(dark green 3), 0x274E13, 0, 75, 0
;action76(dark cyan 3), 0x0C343D, 0, 76, 0
;action77(dark cornflower blue 3), 0x1C4587, 0, 77, 0
;action78(dark blue 3), 0x073763, 0, 78, 0
;action79(dark purple 3), 0x20124D, 0, 79, 0
;action80(dark magenta 3), 0x4C1130, 0, 80, 0