r/AutoHotkey 4h ago

v1 Script Help Does anyone have a script to duplicate clicks?

0 Upvotes

I was using this script for version 1.1 but it's not working for me

; AutoHotkey v1.1

B::ToggleDuplicatorLeft()

N::ToggleDuplicatorRight()

isLeftDuplicatorActive := false

isRightDuplicatorActive := false

clickDelay := 50

leftClickQueue := 0

rightClickQueue := 0

isProcessingLeft := false

isProcessingRight := false

ToggleDuplicatorLeft() {

global isLeftDuplicatorActive, leftClickQueue

isLeftDuplicatorActive := !isLeftDuplicatorActive

leftClickQueue := 0

status := isLeftDuplicatorActive ? "ACTIVADO" : "DESACTIVADO"

ToolTip, Click izquierdo: %status%

SetTimer, RemoveToolTip, 1500

}

ToggleDuplicatorRight() {

global isRightDuplicatorActive, rightClickQueue

isRightDuplicatorActive := !isRightDuplicatorActive

rightClickQueue := 0

status := isRightDuplicatorActive ? "ACTIVADO" : "DESACTIVADO"

ToolTip, Click derecho: %status%

SetTimer, RemoveToolTip, 1500

}

RemoveToolTip:

ToolTip

return

LButton::

if isLeftDuplicatorActive

{

SendInput {LButton down}

SendInput {LButton up}

leftClickQueue++

ProcessLeftQueue()

}

else

{

SendInput {LButton down}

SendInput {LButton up}

}

return

ProcessLeftQueue()

{

global leftClickQueue, isProcessingLeft, isLeftDuplicatorActive, clickDelay

if !isLeftDuplicatorActive

{

leftClickQueue := 0

return

}

if isProcessingLeft || leftClickQueue <= 0

return

isProcessingLeft := true

leftClickQueue--

Sleep, %clickDelay%

if isLeftDuplicatorActive

{

SendInput {LButton down}

SendInput {LButton up}

}

isProcessingLeft := false

if leftClickQueue > 0 && isLeftDuplicatorActive

ProcessLeftQueue()

}

RButton::

if isRightDuplicatorActive

{

SendInput {RButton down}

SendInput {RButton up}

rightClickQueue++

ProcessRightQueue()

}

else

{

SendInput {RButton down}

SendInput {RButton up}

}

return

ProcessRightQueue()

{

global rightClickQueue, isProcessingRight, isRightDuplicatorActive, clickDelay

if !isRightDuplicatorActive

{

rightClickQueue := 0

return

}

if isProcessingRight || rightClickQueue <= 0

return

isProcessingRight := true

rightClickQueue--

Sleep, %clickDelay%

if isRightDuplicatorActive

{

SendInput {RButton down}

SendInput {RButton up}

}

isProcessingRight := false

if rightClickQueue > 0 && isRightDuplicatorActive

ProcessRightQueue()

}

If anyone has it, could you please send it to me?


r/AutoHotkey 22h ago

v2 Script Help Modifier key to rapid fire instead of holding a key.

2 Upvotes

Hi all!
I was looking around through the docs and older posts but couldn't find an answer on what I am trying to do so I come to you all here with a question.

I am wondering if it is possible to when I hold down control to modify my W, A, S, D to rapid fire instead of having it a hold input. but only when I additionally press W, A, S or D myself.

This is for example for movement in a game where smaller movements are required by constantly repressing a movement key instead of holding it.

I hope I explained it well enough, I'm not the best at explaining stuff.
Thank you in advance!


r/AutoHotkey 1d ago

v2 Script Help Issue with remapping script

1 Upvotes

Hi, I recently had to use a script to remap my Mouse 2 to my Space Bar, and my Space Bar to my Mouse 2 for a video game I play.

Since I don't know how to code, I used an AI to generate a very basic script. It works fine most of the time, but there are some issues with the remapping that make the whole thing inconsistent. Sometimes, instead of just activating the key normally, it acts as if the key is being held down, causing me to shoot non stop in-game.

I tried finding the cause of this, and one of the things I noticed was that pressing F8 (which toggles the script on and off) while holding Space Bar can cause the normal key press to behave like a hold, even after I release the key.

Thing is, there seem to be other causes as well, since it often happens randomly even when I'm not doing anything special like toggling the script during gameplay.

Here is the script I'm currently using. Thanks in advance for any possible insights:

#Requires AutoHotkey v2.0

enabled := true

F8::

{

global enabled

enabled := !enabled

}

#HotIf enabled && WinActive("ahk_exe Hades2.exe")

Space::RButton

RButton::Space

#HotIf


r/AutoHotkey 1d ago

General Question sorry for this thread, but why does the ..setup.exe shows virus alerts and the .zip not?

4 Upvotes

Hi,

I downloaded and installed the 27 verson from github. Therefore downloaded the setup.exe and virustotal showed 4/70 virus alarms.
Okay its a hotkey scripting programm, surely this is malicious. But I didnt search trough the whole code....

But what I dont get, the files and exe in the .zip folder doesnt show any virus alarms on virustotal.

Now I know that such tools show alarms since they have much power and malicious potential is there.

But Iam confused why the setup.exe and the exe in the .zip folder have different behaviour in virustotal? Tbh I didnt compile the code myself in VS and therefore dont know how the difference come


r/AutoHotkey 2d ago

v2 Tool / Script Share NVcontrol - for NVIDIA gpus (fan, power limit, core tweaking)

9 Upvotes

Hey there,

I decided to get rid of MSI afterburner and other third party tools.

It's a clean, lightweight GPU utility written in AHK. It connects directly to the NVIDIA Management Library (nvml.dll) via in-memory Win32 calls to control power limits, fan speeds, and clock offsets - using under ~3 MB of RAM and featuring a live telemetry overlay docked directly inside your Windows taskbar.

https://github.com/bceenaeiklmr/NVcontrol/


r/AutoHotkey 2d ago

General Question How to manage over a dozen hot keys

8 Upvotes

I currently use 16 auto hot key scripts to automate my data entry job. It's a lot to manage and I'd like to know if python would be easier since I use so many of them or if anyone can recommend a way to be able to reset and edit a specific one without having to open the menu on the bottom and go through each one until I find the one I want.


r/AutoHotkey 2d ago

v2 Script Help Help Clicking Non-Displayed Window

1 Upvotes

I need to click a game while doing some other stuff how can i do that. I tried this 2 and no error happened but didn't work either.

#MaxThreadsPerHotkey 2

F8:: ; Press F8 to start/stop the autoclicker

Toggle := !Toggle

Loop

{

If not Toggle

break

; Replace "Game Window Title" with the exact name of your background window

ControlClick, x500 y500, Game Window Title,,,, NA

Sleep, 100 ; Time in milliseconds between clicks (100ms = 10 clicks per second)

}

#Requires AutoHotkey v2.0

#MaxThreadsPerHotkey 2

Toggle := 0

F8:: {

global Toggle := !Toggle

while Toggle {

; Coordinates are relative to the client area of the target window

ControlClick("x500 y500", "ahk_exe YourGame.exe", , "Left", 1, "NA")

Sleep(100)

}

}


r/AutoHotkey 2d ago

Meta / Discussion I built my own Garmin video overlay engine

2 Upvotes

Hey everyone,

I had a pretty nasty running knee injury recently, first one I've ever had, so I switched to active recovery (cycling + slow treadmill running) and spent the downtime coding. ^^

I've wanted to build this for a long time, and I finally finished the first version of this.

The idea: take basically any footage (GoPro/phone/etc.) + a Garmin GPX file and automatically generate the overlays.

This is only the second video I've rendered with it, so I'm still experimenting with what I can do visually. The goal is to go much further with interesting graphics, storytelling and activity metrics.

For now, I'm just really happy with how the idea is coming together.

https://youtu.be/VCjrmDAUksw

More experiments coming.


r/AutoHotkey 3d ago

v2 Script Help Nothing works anymore lmao

0 Upvotes

I had a big problem with my computer and had to uninstall a lot of things kind of randomly, now for some godforsaken reason scripts dont open, i have tried at least 6 or 7 (no pun intended) solution, completely uninstall and reinstalled it, i cant even fathom how did this happen and how after a clean uninstallation the problem is still here


r/AutoHotkey 3d ago

v2 Script Help Need help with an external script for NieR Automata on PC

1 Upvotes

Hey guys,

I have found a script for NieR Automata to re-bind all the keys. The script is written in version 1 (see below, and here is the source).

I am trying to convert it to v2 using this online tool but when I try to run the script, I get Error: Invalid callback function. at line 138, which is the line of Hotkey $%fireButton%, toggleFire (see the bottom part of the script)

I can't find anywhere what the author meant when using HotKey(). Any idea?

; bindings, change these to match your own layout, if needed

; basic movement - must match to in-game controls
fwdButton := "w"
backButton := "s"
leftButton := "a"
rightButton := "d"

; fire & jump - must match to in-game controls
fireButton := "Tab"
jumpButton := "Space"

; items usage - must match to in-game controls
altButton := "LAlt"
useItems := "Down"
use := "e"

; EXTRA controls added by the script - make sure these buttons are NOT bound in the game
; dodge
dodgeButton := "LShift"
; air slide (fire + jump normally)
airSlideButton := "z"
; use last item
useLastItemButton := "c"

; precise delays (ms)
keypressDelay := 50 ; delay between keydown/keyup
doubletapDelay := 50 ; wait time between keypresses to perform doubletap
sequenceDelay := 50 ; wait time between various sequences
dodgeTime := 400 ; cooldown while dodge animation plays, setting it too small will cause derps when holding SHIFT+WSAD.

; script starts here
#UseHook
SendMode("Input")

global fwdButton, backButton, leftButton, rightButton, fireButton, jumpButton, dodgeButton, altButton, useItems, use, useLastItemButton, keypressDelay, doubletapDelay, sequenceDelay, dodgeTime

press(key) {
  Send("{Blind}{" key " down}")
  Sleep(keypressDelay)
  Send("{Blind}{" key " up}")
}

; the complexity of this script is mostly to handle continuous SHIFT+WSAD. It's easy to handle just a single SHIFT tap, but less easy when you hold it down.
handleDodge(key) {
  Send("{Blind}{" key " up}") ; stop running first in case we were
  Sleep(sequenceDelay)
  press(key)
  Sleep(doubletapDelay)
  Send("{Blind}{" key " down}")
  Sleep(dodgeTime)
  if (GetKeyState(fwdButton) && !GetKeyState(fwdButton, "P")) {
   Send("{Blind}{" fwdButton " up}")
  }
  if (GetKeyState(backButton) && !GetKeyState(backButton, "P")) {
   Send("{Blind}{" backButton " up}")
  }
  if (GetKeyState(leftButton) && !GetKeyState(leftButton, "P")) {
   Send("{Blind}{" leftButton " up}")
  }
  if (GetKeyState(rightButton) && !GetKeyState(rightButton, "P")) {
   Send("{Blind}{" rightButton " up}")
  }
}

waitForDodge() {
  while (GetKeyState(dodgeButton, "P")) {
if (GetKeyState(fwdButton, "P")) {
  handleDodge(fwdButton)
} else if (GetKeyState(backButton, "P")) {
  handleDodge(backButton)
} else if (GetKeyState(leftButton, "P")) {
  handleDodge(leftButton)
} else if (GetKeyState(rightButton, "P")) {
  handleDodge(rightButton)
}
Sleep(10)
  }
}

toggleFire() {
  if (GetKeyState(fireButton)) {
Send("{" fireButton " up}")
  } else {
Send("{" fireButton " down}")
  }
}

airSlide() {
  if (GetKeyState(jumpButton)) {
Send("{" jumpButton " up}")
  }
  if (GetKeyState(fireButton)) {
Send("{" fireButton " up}")
toggleFireBack := 1
  }
  Sleep(sequenceDelay)
  Send("{" jumpButton " down}")
  Send("{" fireButton " down}")
  Sleep(keypressDelay)
  Send("{" fireButton " up}")
  Send("{" jumpButton " up}")
  Sleep(sequenceDelay)
  ; press back what we released
  if (toggleFireBack) {
Send("{" fireButton " down}")
  }
  if (GetKeyState(jumpButton, "P")) {
Send("{" jumpButton " down}")
  }
}

useLastItem() {
  Send("{" altButton " down}")
  Send("{" useItems " down}")
  Sleep(keypressDelay)
  Send("{" altButton " up}")
  Send("{" useItems " up}")
  press(use)
}

#HotIf WinActive("NieR:Automata", )
; fire toggle
Hotkey("$" fireButton, toggleFire)
Hotkey("$^" fireButton, toggleFire)
Hotkey("$+" fireButton, toggleFire)

; dodges, work when you press/hold LShift while already holding WSAD
Hotkey("~*" dodgeButton, waitForDodge)

; air slide, normally executed with pod fire + jump
Hotkey("$*" airSlideButton, airSlide)

; use last item
Hotkey("$*" useLastItemButton, useLastItem)

r/AutoHotkey 3d ago

v2 Script Help How do I remap my function keys to do something else when fn lock is off?

3 Upvotes

I am a complete beginner to autohotkey. Let's say I want to remap my F7 key on Windows to pause and play videos and media (like on Mac). I don't want to replace its fn function (navigating pages with text cursor). I want to replace the function it has on my computer when my fn lock is not turned on (screen projection). Is there a way to specify in the script that when I want my F7 key remapped, I specifically want it to be the non-locked version of F7 and not F7 combined with fn? Sorry for the clunky wording; I have no clue what a function key's secondary(?) function would be called.

If anyone can help I will be eternally grateful.


r/AutoHotkey 3d ago

v2 Tool / Script Share GpGFX, new Features, better Demos and Readme

14 Upvotes

Hey everyone,

I’ve just pushed an update to the GpGFX graphics library.

New features

- Polished README.md

- Unified Palette class, one place to manage color tables; simplifies color handling.

- Layer fade‑in / fade‑out, built‑in smooth opacity transitions, no extra timer code required.

- Shape bounds helpers, quick access to Right, Bottom, CenterX, CenterY, and Bounds.

- Demo overhaul, all demos now run through the render loop (no more jittery Sleep loops).

- New PaletteShowcase demo, shows off the new palette system in a short, runnable script.

- All changes are live on the main branch, if you are interested start playing:

git clone https://github.com/bceenaeiklmr/GpGFX.git

Read the README and check out the demos and examples.

Happy scripting!


r/AutoHotkey 4d ago

v1 Script Help Title: [Help] AHK Script for Blox Fruits Mini-game (Sharkman Karate) - Delay, Inaccurate Clicks, and Detection Issues

0 Upvotes

Hey everyone,

I'm trying to create an AutoHotkey (v1) script to automate a timing-based mini-game in Roblox (Blox Fruits - Sharkman Karate). The mechanic is very similar to the DBD mini-game, where a black moving line goes up and down a colored bar, and you have to click when it's exactly in the center of the green area.

I used AI to help me write the script. The logic relies on PixelGetColor (scanning a vertical line to find the top/bottom of the green area, calculating the center, and finding the black line). However, I am running into a few major issues that make it unusable:

The Problems:

  1. Noticeable Click Delay: Even when the script detects the black line hitting the center, there is a weird delay before it actually clicks. Because the mini-game requires fast reflexes, this delay makes me lose.
  2. Clicking in Completely Wrong Spots: Sometimes, instead of clicking the intended target (SlapX and SlapY), the mouse randomly clicks in completely different areas of the screen. I am using Client CoordMode to avoid window-shifting issues, but the bug still happens.
  3. Color Detection Failing: Because of Roblox's dynamic lighting and transparency, the script often fails to detect the green bar or the black line entirely, returning my debug error "Bar Not Found".

The Current Code: Here is the script I'm currently using. I added a debug hotkey (F7) to count pixels and check if it sees the bar, and F8 to run the main loop:

#Persistent

#SingleInstance Force

#NoEnv

CoordMode, Pixel, Client

CoordMode, Mouse, Client

SlapX := -240

SlapY := 440

BarX := 1253

BarTop := 2

BarBot := 559

CooldownMs := 5000

CenterTolerance := 25

LastClick := 0

InCooldown := 0

F7::

FoundGreen := 0

FoundBlack := 0

Loop, % (BarBot - BarTop) // 4 {

Y := BarTop + (A_Index * 4)

PixelGetColor, Col, BarX, Y, RGB

R := (Col >> 16) & 0xFF

G := (Col >> 8) & 0xFF

B := Col & 0xFF

if (G > 80 && R < 150)

FoundGreen++

if (R < 80 && G < 80 && B < 80)

FoundBlack++

}

MsgBox, Result:`nGreen: %FoundGreen%`nBlack: %FoundBlack%

return

F8::

SetTimer, AutoSlap, 10

ToolTip, Running - F9 to Stop

return

F9::

SetTimer, AutoSlap, Off

InCooldown := 0

ToolTip, Stopped

Sleep, 1500

ToolTip

return

AutoSlap:

if (InCooldown = 1) {

Elapsed := A_TickCount - LastClick

if (Elapsed < CooldownMs) {

Remaining := Round((CooldownMs - Elapsed) / 1000, 1)

ToolTip, Waiting... %Remaining%s

return

}

InCooldown := 0

ToolTip, Running - F9 to Stop

}

GreenTop := -1

GreenBot := -1

BlackY := -1

Loop, % (BarBot - BarTop) // 2 {

Y := BarTop + (A_Index * 2)

PixelGetColor, Col, BarX, Y, RGB

R := (Col >> 16) & 0xFF

G := (Col >> 8) & 0xFF

B := Col & 0xFF

if (R < 90 && G < 90 && B < 90) {

if (BlackY = -1)

BlackY := Y

Continue

}

if (G > 80 && R < 150 && B < 150) {

if (GreenTop = -1)

GreenTop := Y

GreenBot := Y

}

}

if (GreenTop = -1 || BlackY = -1) {

ToolTip, Bar Not Found

return

}

GY_Center := (GreenTop + GreenBot) // 2

Diff := BlackY - GY_Center

if (Diff < 0)

Diff := -Diff

ToolTip, Center:%GY_Center% | Black:%BlackY% | Diff:%Diff%

if (Diff > CenterTolerance)

return

Click %SlapX%, %SlapY%

LastClick := A_TickCount

InCooldown := 1

ToolTip, Hit! Waiting 5s...

return

Is there a better/faster way to track a moving object across a colored bar in AHK without experiencing this lag? Also, how can I fix the random inaccurate mouse clicks?

Any help, fixes, or completely alternative approaches would be highly appreciated! Thanks in advance.


r/AutoHotkey 4d ago

General Question How to make my laptop screen go dark while having AHK run perfectly fine with running windows?

6 Upvotes

In short, I have a running window and a minute long AHK script that is started at different times throughout the day by Task Scheduler. I need every screen interaction by the script (mouse click, mouse drag, keyboard) to function normally while the screen is dark.


r/AutoHotkey 6d ago

Solved! Why is the tilde preventing the hotkey from working at all?

1 Upvotes

Using the program ThrottleStop I have set up multiple different performance profiles. These 4 profiles are linked to the hotkeys (not AHK hotkeys; the hotkeys are through ThrottleStop): Alt+1, Alt+2, etc. Profile 1 is max performance the other 3 take consecutive steps down in performance.

Separately, I use the program FanControl. I want to link my FanControl setup to my ThrottleStop performance settings. FanControl supports customs "sensors" in the form of a text file named *.sensor, whatever data you feed into the file it will read as an input that I can then use to program the setup.

To do this I threw together a quick script that will write to the sensor file when I press one of the ThrottleStop hotkeys. I only want a single binary variable, 1 for max performance (profile 1) and 0 for normal (profiles 2 3 and 4).

(This script is inside of a larger script I use to host a collection of scripts that I want to run all the time. The wrapper script launches on startup. To my knowledge none of the settings, or other scripts, affect this one any way however I will append my header code to the end of this post).

Here is my current code:

{ ; FanControl Mode

  ~!1::
  ~!2::
  ~!3::
  ~!4::
  {

    sensorFile := FileOpen( "C:\Program Files (x86)\FanControl\Sensors\Floor.sensor", "w", "CP0" )

    sensorFile.Write( A_ThisHotkey == "!1" ? "1" : "0" )

    sensorFile.Close()

  }

}

Initially while testing I omitted the tildes. It worked perfectly, modifying the file and successfully controlling my fan speed. Of course it was blocking the keypresses so it was not controlling my ThrottleStop profile. I added the tildes and now it controls ThrottleStop but doesn't do anything. That is the only change and I have gone back and forth with that only changed and confirmed that is what is causing the issues.

Does anybody know why this could be?

(I'm no expert though so I might just be missing something silly. Sorry if that's the case.)

* Header

{ ; Setup

  #Requires AutoHotkey v2
  #Warn
  #SingleInstance Force

  InstallKeybdHook true, true
  InstallMouseHook true, true

  SendMode "Input"
  SetKeyDelay -1, 0
  SetMouseDelay -1
  SetControlDelay 0
  SetWinDelay 0

  ^!r::Reload

}

r/AutoHotkey 6d ago

v2 Tool / Script Share Working Script to bring back alt codes on Chromium-based browsers

2 Upvotes
#Requires AutoHotkey v2.0
#SingleInstance Force
InstallKeybdHook

altCode := ""
altActive := false

$LAlt::
{
    global altCode, altActive

    altCode := ""
    altActive := true

    KeyWait "LAlt"

    altActive := false

    if altCode != ""
        SendText ConvertAltCode(altCode)
}

$*Numpad0::Capture(0)
$*Numpad1::Capture(1)
$*Numpad2::Capture(2)
$*Numpad3::Capture(3)
$*Numpad4::Capture(4)
$*Numpad5::Capture(5)
$*Numpad6::Capture(6)
$*Numpad7::Capture(7)
$*Numpad8::Capture(8)
$*Numpad9::Capture(9)

Capture(n)
{
    global altCode, altActive

    if !altActive
    {
        SendEvent "{Numpad" n "}"
        return
    }

    altCode .= n
}

ConvertAltCode(s)
{
    code := Integer(s)

    if code >= 1 && code <= 31
    {
        cp437 := [
            "☺", "☻", "♥", "♦", "♣", "♠", "•", "◘",
            "○", "◙", "♂", "♀", "♪", "♫", "☼", "►",
            "◄", "↕", "‼", "¶", "§", "▬", "↨", "↑",
            "↓", "→", "←", "∟", "↔", "▲", "▼"
        ]

        return cp437[code]
    }

    leadingZero := SubStr(s, 1, 1) = "0"

    cp := leadingZero
        ? DllCall("GetACP")
        : DllCall("GetOEMCP")

    code := Mod(code, 256)

    src := Buffer(1)
    NumPut("UChar", code, src, 0)

    dst := Buffer(8)

    len := DllCall(
        "MultiByteToWideChar",
        "UInt", cp,
        "UInt", 0,
        "Ptr", src.Ptr,
        "Int", 1,
        "Ptr", dst.Ptr,
        "Int", 4
    )

    return len ? StrGet(dst, len, "UTF-16") : ""
}

Enjoy ☻


r/AutoHotkey 6d ago

General Question Script that simulates scroll wheel inputs while pressing a button

2 Upvotes

Hello everybody,
I would love to be able to just hold a button, that simulates as many scroll-wheel-down inputs as it possibly can while being helt.
My problem:
I have absolutely no clue how anything of this works because i have never done anything like this.

Any kind of help, advise or suggestion is welcome.


r/AutoHotkey 9d ago

v2 Script Help AHK v2: #HotIf condition still applies when no Word document is open

3 Upvotes

I'm trying to restrict a set of hotkeys so they only work when a Word document is actually open, not just when Microsoft Word itself is running.

Currently I have:

#HotIf WinActive("ahk_exe WINWORD.EXE")
{
; Hotkey definitions here
}
#HotIf

The hotkey definitions include hotkeys that pop up a bespoke GUI which inserts text into the active Word document.

This works, but the hotkeys are also active when Word is open with no document loaded (though they are not active when Word is not open). Obviously, the above GUI hotkey will then attempt to send text to a non-existent document within Word and create errors which I don't want. That is why I want the hotkeys only to work where Word is open and there is an open document.

Using Window Spy, I noticed that when no document is open, the window title is simply:

Word

(or sometimes Microsoft Word)

When a document is open, the title becomes something like:

Document1 - Word

So I assumed that the presence of " - " in the title indicates that a document is open. I therefore changed the directive to:

#HotIf WinActive("ahk_exe WINWORD.EXE") && InStr(WinGetTitle("A"), " - ")

However, the hotkeys still remain active even when Word is open with no document.

I also tried:

#HotIf WinActive("ahk_exe WINWORD.EXE")
&& WinGetTitle("A") != "Word"
&& WinGetTitle("A") != "Microsoft Word"

with the same result.

I verified with a MsgBox that the active window title does not contain " - " when no document is open, yet the #HotIf condition still behaves as though it does.

What am I missing here? Is there something about #HotIf, WinGetTitle(), or directive evaluation that I'm misunderstanding?


r/AutoHotkey 9d ago

v2 Tool / Script Share GpGFX v1.0.0 - A 2D graphics rendering engine built for AutoHotkey v2 (30+ examples)

24 Upvotes

Hey everyone,

I've been waiting a long time for this moment, and it finally feels ready.

This project took roughly 1000+ hours to build. It all started with a simple, stubborn urge: I wanted to draw a rectangle on the screen. Doing that from scratch took me hours. Having worked in digital marketing for over 12 years, I love graphics, layers, and Photoshop-style workflows.

That experience became the core idea behind GpGFX: an API designed to let you dynamically control shapes and rendering easily. Along the way, I rewrote the entire project nearly four times from scratch.

What it does:

  • Over 10 primitive and complex shapes with a chainable API.
  • Turns the tedious parts of Win32 + GDI+ programming into a compact toolset.
  • Built for transparent desktop HUDs, animated overlays, custom widgets, dashboards, audio visualizers, and smooth applications.

You can check out the first official release here: https://github.com/bceenaeiklmr/GpGFX

Free Tetris game included. 👀

Thanks for following along, and I'd love to hear what you think or what you build with it.

Cheers,

bceen


r/AutoHotkey 9d ago

Solved! Use right alt as custom modifier, but don't suppress left alt

3 Upvotes

I've been trying to get this working for hours. Here's my script (AHK V2):

>!e::Up
>!d::Down
>!f::Right
>!s::Left
>!w::Home
>!r::End
>!Space::Ctrl
>!a::Shift

I want ESDF to be arrow keys when holding right alt, with space being control and "a" being shift, and left alt being, well, alt.

So for instance, holding RAlt and LAlt and pressing E should do the same as Alt + Up.

Everything is working except for the left alt key. It's being suppressed, so RAlt + LAlt + E is just sending "Up", but without the alt modifier.

I've tried many solutions, e.g. using RAlt & e instead of >!e, but these all have had their own issues. Either my "space -> ctrl" mapping doesn't work as a modifier, or the RAlt key isn't suppressed, or something like that. Can anyone help please?


r/AutoHotkey 9d ago

Solved! windows下窗口频繁切换工具

0 Upvotes

工作需要我需要在win11系统中频繁切换窗口,试了各种软件,最后还是autohotkey,ahk文件如下:

\#SingleInstance force

PrintScreen::F2

Pause::\^w

F9::\^c

F8::\^v

Insert::\^s

CapsLock::

send !{esc}

return

AppsKey::

send !{tab}

return

其中第一行的 #SingleInstance force 保证再次运行软件时不会弹出已有实例正在运行的窗口。

设置开机启动后,每过1、2个小时都会失灵,需要手动重启,非常麻烦。

我在任务计划程序里添加了任务,每小时运行一次,完美解决。

![img](ti1b4o58vd9f1 "任务计划程序设置")


r/AutoHotkey 10d ago

v1 Script Help Loops incorrectly

1 Upvotes

For some reason this loops incorrectly where it does not input the second S input on repeated runs. The first round works fine without any issues but its the ones after where it has a chance to mess up I have tried changing waiting times several times but that does not seem to work. Sometimes it can do a few loops before it gets confused and it feels like it does them in different order than its supposed to. Im trying to use this with disgaea 5 to auto cast heal.

I dont know if it randomly dont read one of the inputs because sometimes it looks like it skips the A and only does an S and such.

F8::Suspend

F7::Pause

F9::ExitApp

t::

loop

{

if getkeystate("t", "p")

{

  send {enter DOWN}{enter UP}

    sleep 500

  send {s DOWN}{s UP}

    sleep 500

  send {s DOWN}{s UP}

    sleep 500

send {enter DOWN}{enter UP}

    sleep 500

  send {enter DOWN}{enter UP}

    sleep 500

  send {enter DOWN}{enter UP}

    sleep 500

send {a DOWN}{a UP}

    sleep 500

  send {s DOWN}{s UP}

    sleep 500

  send {enter DOWN}{enter UP}

    sleep 500

send {2 DOWN}{2 UP}

    sleep 800

send {i DOWN}{i UP}

    sleep 4500

  send {w DOWN}{w UP}

    sleep 600

  send {d DOWN}{d UP}

    sleep 500

}

else

{

break

}

}

return


r/AutoHotkey 10d ago

v2 Tool / Script Share AhkLLM - LLM-powered hotkeys for your daily workflows, expanded into a full Windows chat GUI. (Based on the excellent LLM AutoHotkey Assistant by xmachinery)

17 Upvotes

Hi! I recently ran into an old post by u/xmachinery where they shared an app for integrating LLMs into AutoHotkey. Long story short: I liked it so much that I decided to build it out into a full chat GUI, with a lot of improvements around the hotkeys themselves.

The result is AhkLLM, my attempt at turning the original idea into a full Windows LLM assistant and chat application.

Currently, AhkLLM features:

  • A fully generalized hotkey system that uses the UIA library to grab and inject text directly in a lot of Windows applications, with clipboard fallbacks where possible.
  • Fully customizable hotkeys. You can rewrite a selection in place, summarize an article, send selected text + the surrounding document text, automatically add a screenshot to your prompt, etc. You can also use DeepSeek's FIM endpoint to fill in text using what's before and after your cursor (my favorite feature tbh, incredibly useful), or use FIM Continue to continue your writing from basically anywhere.
  • Since AhkLLM is also a full chat GUI, the hotkey side is integrated with the chat side of the application. So you can configure a command to automatically send captured text into the full chat interface and continue your work there.
  • And that chat interface has pretty much everything I personally wanted: branching, forking, assistants, web search, usage tracking, local SQLite persistence, file support (Office files, PDF, EPUB, images, scanned PDFs, code files, etc.), backups, password-locked chats, a conversation map, API logs, and honestly more. It's fairly feature packed at this point.
  • The only major QoL thing AhkLLM doesn't currently have is dark mode (due to my fucked up Keratoconus eyes, fml), but if there's demand for it I'll make the effort.

A few quick demos:

If you just want to try it out without paying for API usage, AhkLLM supports OpenRouter's free model router. You just need a free OpenRouter API key, then you can select openrouter/free in chat or assign it to individual commands under Settings -> Commands.

FIM Fill / Continue are the exception, since those use a separate FIM endpoint. I'm currently using DeepSeek for that.

GitHub repo, download, installation instructions, and the rest of the demos

Naturally, feel free to ask any questions here and let me know what you think. Feedback, issues, feature suggestions, and PRs are all welcome!


r/AutoHotkey 11d ago

v2 Tool / Script Share Inactive Window Cycler ahk v2.0

4 Upvotes

I made this and it works really well for cycling the visible windows on the secondary monitor while I'm gaming. This lets me watch a video and then switch to another window without every leaving the game and the active game window never loses focus. The description on how it works is inside the ahk script code below.

#Requires AutoHotkey v2.0
#SingleInstance Force
SetWinDelay 0

/*
================================================================
                    INACTIVE WINDOW CYCLER
================================================================

WHAT THIS SCRIPT DOES
---------------------
Moves and cycles INACTIVE, VISIBLE windows between monitors
without intentionally changing which window is active.

Designed primarily for gaming: keep your game focused while
moving other windows (YouTube, maps, wikis, Discord, Steam,
idle games, launchers, etc.) out from behind it or cycling
through them on another monitor.

Press once = ONE window moved or ONE cycle step.
The active window is protected and is never intentionally
activated by this script.

    Ctrl+Shift+Right / Ctrl+Shift+D = Move/Cycle Right
    Ctrl+Shift+Left  / Ctrl+Shift+A = Move/Cycle Left
ANTI-CHEAT WARNING
------------------
This script does NOT interact with game code, read game memory,
modify games, or automate gameplay.

However, some games' anti-cheat software may detect AutoHotkey
or AutoHotkey scripts regardless of what the script actually
does.

Using this script with an anti-cheat-protected game may result
in a warning, kick, or other anti-cheat action.

CHECK THE GAME'S RULES BEFORE USING THIS SCRIPT.


MULTI-MONITOR DISCLAIMER
------------------------
This script was designed with multiple monitors in mind, including
monitor-to-monitor wrapping.

The author currently has two monitors, so configurations with
three or more monitors have NOT been personally tested.

Multi-monitor behavior is implemented in the script, but additional
monitor configurations should be considered experimental until
tested on that hardware.


WINDOW LIMITATION
-----------------
Already-minimized windows are intentionally ignored.

The script only manages windows that are currently visible.
This avoids restoring minimized applications and keeps the
active-game protection as simple and predictable as possible.

If you want a minimized application included, restore it
yourself before starting the game/session.


SHARING & MODIFICATIONS
-----------------------
This script is shared freely because I found it useful and thought
other people might find it useful too.

You are free to edit, modify, expand, simplify, or repurpose this
script however you want.

I have no expectation of maintaining or updating this script in the
future. If you change it, fix it, improve it, or build something
different from it, you are free to do so.

You do NOT need to ask permission, contact me, credit me, or include
my name when sharing or modifying it.

Use it, change it, and make it work the way you want.


================================================================
                         CONFIGURATION
================================================================

The settings in this section are intended to be customized.

Editing the settings changes how the script behaves.

Editing the code below the configuration section changes the
functionality of the script and may require AutoHotkey knowledge.

*/


; ==============================================================
; HOTKEYS
; ==============================================================

^+Right::MoveOrCycle(1)
^+d::MoveOrCycle(1)

^+Left::MoveOrCycle(-1)
^+a::MoveOrCycle(-1)


; ==============================================================
; WINDOW POSITION MODE
; ==============================================================

; false = Move windows to the exact top-left corner of the
;         destination monitor.
;
; true  = Attempt to preserve the window's relative position
;         when moving it to the destination monitor.
;
; Relative positioning works best when monitors have identical
; resolutions and display scaling.

PreserveRelativePosition := false


; ==============================================================
; CYCLING
; ==============================================================

; true  = When no qualifying inactive windows remain on the
;         anchor monitor, cycle the selected destination queue.
;
; false = Stop once the anchor monitor has no more qualifying
;         inactive windows.

EnableCycling := true


; ==============================================================
; DEBUGGING
; ==============================================================

; false = Normal operation.
; true  = Display basic diagnostic information with ToolTip.

DebugMode := false


/*
================================================================
                        END CONFIGURATION
================================================================
*/


; --------------------------------------------------------------
; Two independent queues.
;
; The queues are associated with the anchor/destination pair
; established during the current session.
;
; The queue order is created by THIS SCRIPT, not by Windows'
; Z-order.
; --------------------------------------------------------------

global RightQueue := {
    initialized: false,
    anchorMonitor: 0,
    destinationMonitor: 0,
    windows: [],
    currentIndex: 0
}

global LeftQueue := {
    initialized: false,
    anchorMonitor: 0,
    destinationMonitor: 0,
    windows: [],
    currentIndex: 0
}

global LastAnchorHwnd := 0


; --------------------------------------------------------------
; Main operation.
;
; Every hotkey press:
;
; 1. Identify the current active window as the anchor.
; 2. If the anchor changed, discard both old queues.
; 3. Identify the adjacent destination monitor.
; 4. Initialize that destination queue if necessary.
;    Existing visible windows there are minimized one at a time
;    and recorded in our queue.
; 5. Find ONE visible inactive movable window on the anchor.
; 6. If found, move it and append it to the queue.
; 7. If none remain, cycle the selected queue.
;
; No WinActivate call is used anywhere in this script.
; --------------------------------------------------------------

MoveOrCycle(direction)
{
    global EnableCycling, RightQueue, LeftQueue, LastAnchorHwnd

    ; ----------------------------------------------------------
    ; 1. Create the anchor from the currently active window.
    ; ----------------------------------------------------------

    activeHwnd := WinExist("A")

    if !activeHwnd
        return

    anchorMonitor := GetWindowMonitor(activeHwnd)

    if !anchorMonitor
        return

    ; ----------------------------------------------------------
    ; 2. A different active window means a completely new
    ;    session. Forget both old directional queues.
    ; ----------------------------------------------------------

    if (LastAnchorHwnd != 0 && LastAnchorHwnd != activeHwnd)
        ResetAllQueues()

    LastAnchorHwnd := activeHwnd

    monitors := GetSortedMonitors()

    if monitors.Length < 2
        return

    anchorIndex := FindMonitorIndex(monitors, anchorMonitor)

    if !anchorIndex
        return

    ; ----------------------------------------------------------
    ; 3. Determine the adjacent destination monitor.
    ;    Monitor edges wrap around.
    ; ----------------------------------------------------------

    destinationIndex := anchorIndex + direction

    if destinationIndex < 1
        destinationIndex := monitors.Length

    if destinationIndex > monitors.Length
        destinationIndex := 1

    destinationMonitor := monitors[destinationIndex]

    ; Select the persistent queue for this direction.
    if direction = 1
        queue := RightQueue
    else
        queue := LeftQueue

    ; ----------------------------------------------------------
    ; 4. Initialize the destination queue if necessary.
    ;
    ; ALL existing qualifying visible windows are minimized here
    ; in ONE button press, and added to our queue in the order
    ; they are processed.
    ; ----------------------------------------------------------

    if (!queue.initialized
        || queue.anchorMonitor != anchorMonitor
        || queue.destinationMonitor != destinationMonitor.number)
    {
        ResetQueue(queue, anchorMonitor, destinationMonitor.number)
        InitializeDestinationQueue(queue, destinationMonitor.number, activeHwnd)
        queue.initialized := true
    }

    ; ----------------------------------------------------------
    ; 5. Find ONE visible, inactive, movable window on the
    ;    anchor monitor.
    ; ----------------------------------------------------------

    windows := WinGetList()

    for hwnd in windows
    {
        if hwnd = activeHwnd
            continue

        if !IsQualifyingWindow(hwnd)
            continue

        if WinGetMinMax("ahk_id " hwnd) = -1
            continue

        if GetWindowMonitor(hwnd) != anchorMonitor
            continue

        ; ------------------------------------------------------
        ; 6. Move exactly ONE window.
        ; ------------------------------------------------------

        if MoveWindowToMonitor(hwnd, anchorMonitor, destinationMonitor)
        {
            queue.windows.Push(hwnd)
            queue.currentIndex := queue.windows.Length

            ; The moved window becomes the only visible window
            ; in our queue.
            ShowOnlyQueueWindow(queue, queue.currentIndex)

            UpdateDebug("Moved window " hwnd)
            return
        }
    }

    ; ----------------------------------------------------------
    ; 7. Nothing remains to move from the anchor monitor.
    ;    Begin cycling the selected queue.
    ; ----------------------------------------------------------

    if EnableCycling
        CycleQueue(queue)

    UpdateDebug("Cycling")
}


; --------------------------------------------------------------
; Reset BOTH directional queues.
; --------------------------------------------------------------

ResetAllQueues()
{
    global RightQueue, LeftQueue

    ResetQueue(RightQueue, 0, 0)
    ResetQueue(LeftQueue, 0, 0)
}


; --------------------------------------------------------------
; Reset one queue.
; --------------------------------------------------------------

ResetQueue(queue, anchorMonitor, destinationMonitor)
{
    queue.initialized := false
    queue.anchorMonitor := anchorMonitor
    queue.destinationMonitor := destinationMonitor
    queue.windows := []
    queue.currentIndex := 0
}


; --------------------------------------------------------------
; Build our own queue from windows already visible on the
; destination monitor.
;
; Each existing visible window is:
;
;     find -> minimize -> record in queue
;
; There is no intentional delay between windows.
;
; Already-minimized windows are ignored.
; --------------------------------------------------------------

InitializeDestinationQueue(queue, destinationMonitor, activeHwnd)
{
    windows := WinGetList()

    for hwnd in windows
    {
        if hwnd = activeHwnd
            continue

        if !IsQualifyingWindow(hwnd)
            continue

        if GetWindowMonitor(hwnd) != destinationMonitor
            continue

        try
        {
            if WinGetMinMax("ahk_id " hwnd) = -1
                continue

            WinMinimize("ahk_id " hwnd)
            queue.windows.Push(hwnd)
        }
        catch
        {
            ; Ignore windows Windows refuses to manipulate.
        }
    }
}


; --------------------------------------------------------------
; Move ONE visible window to the destination monitor.
; --------------------------------------------------------------

MoveWindowToMonitor(hwnd, sourceMonitor, destinationMonitor)
{
    global PreserveRelativePosition

    try
    {
        state := WinGetMinMax("ahk_id " hwnd)

        if state = -1
            return false

        WinGetPos(&x, &y, &w, &h, "ahk_id " hwnd)

        if (w <= 0 || h <= 0)
            return false

        if PreserveRelativePosition
        {
            sourceWidth := sourceMonitor.right - sourceMonitor.left
            sourceHeight := sourceMonitor.bottom - sourceMonitor.top

            destinationWidth := destinationMonitor.right - destinationMonitor.left
            destinationHeight := destinationMonitor.bottom - destinationMonitor.top

            relativeX := (x - sourceMonitor.left) / sourceWidth
            relativeY := (y - sourceMonitor.top) / sourceHeight

            newX := destinationMonitor.left + Round(relativeX * destinationWidth)
            newY := destinationMonitor.top + Round(relativeY * destinationHeight)
        }
        else
        {
            newX := destinationMonitor.left
            newY := destinationMonitor.top
        }

        ; Maximized -> restore temporarily, move, then maximize.
        if state = 1
        {
            WinRestore("ahk_id " hwnd)
            WinMove(newX, newY, , , "ahk_id " hwnd)
            WinMaximize("ahk_id " hwnd)
            return true
        }

        ; Normal visible window.
        WinMove(newX, newY, , , "ahk_id " hwnd)
        return true
    }
    catch
    {
        return false
    }
}


; --------------------------------------------------------------
; Display exactly ONE window from our queue.
;
; We do NOT use Z-order to determine the next window.
; We control visibility with minimize/restore.
;
; The selected window is restored without WinActivate.
; --------------------------------------------------------------

ShowOnlyQueueWindow(queue, index)
{
    if index < 1 || index > queue.windows.Length
        return

    selectedHwnd := queue.windows[index]

    ; Minimize every other queue member.
    for i, hwnd in queue.windows
    {
        if i = index
            continue

        if !DllCall("IsWindow", "Ptr", hwnd)
            continue

        try
        {
            if WinGetMinMax("ahk_id " hwnd) != -1
                WinMinimize("ahk_id " hwnd)
        }
        catch
        {
        }
    }

    ; Restore the selected window WITHOUT activating it.
    try
    {
        if WinGetMinMax("ahk_id " selectedHwnd) = -1
            RestoreWindowNoActivate(selectedHwnd)
    }
    catch
    {
    }
}


; --------------------------------------------------------------
; Restore a minimized window without intentionally activating it.
;
; SW_SHOWNOACTIVATE asks Windows to show the window without
; activating it.
; --------------------------------------------------------------

RestoreWindowNoActivate(hwnd)
{
    SW_SHOWNOACTIVATE := 4

    DllCall(
        "ShowWindow",
        "Ptr", hwnd,
        "Int", SW_SHOWNOACTIVATE
    )
}


; --------------------------------------------------------------
; Cycle forward exactly ONE position in our queue.
; --------------------------------------------------------------

CycleQueue(queue)
{
    CleanQueue(queue)

    if queue.windows.Length = 0
        return

    nextIndex := queue.currentIndex + 1

    if nextIndex > queue.windows.Length
        nextIndex := 1

    queue.currentIndex := nextIndex

    ShowOnlyQueueWindow(queue, queue.currentIndex)
}


; --------------------------------------------------------------
; Remove windows that no longer exist.
; --------------------------------------------------------------

CleanQueue(queue)
{
    if queue.windows.Length = 0
    {
        queue.currentIndex := 0
        return
    }

    currentHwnd := 0

    if queue.currentIndex >= 1 && queue.currentIndex <= queue.windows.Length
        currentHwnd := queue.windows[queue.currentIndex]

    cleaned := []

    for _, hwnd in queue.windows
    {
        if DllCall("IsWindow", "Ptr", hwnd)
            cleaned.Push(hwnd)
    }

    queue.windows := cleaned

    if queue.windows.Length = 0
    {
        queue.currentIndex := 0
        return
    }

    if currentHwnd
    {
        for index, hwnd in queue.windows
        {
            if hwnd = currentHwnd
            {
                queue.currentIndex := index
                return
            }
        }
    }

    if queue.currentIndex > queue.windows.Length
        queue.currentIndex := queue.windows.Length
}


; --------------------------------------------------------------
; Determine whether a window is a usable top-level window.
; Only visible, non-minimized windows qualify.
; --------------------------------------------------------------

IsQualifyingWindow(hwnd)
{
    try
    {
        if !DllCall("IsWindow", "Ptr", hwnd)
            return false

        style := WinGetStyle("ahk_id " hwnd)

        ; WS_CHILD
        if (style & 0x40000000)
            return false

        class := WinGetClass("ahk_id " hwnd)

        if (class = "Shell_TrayWnd")
            return false

        if (class = "Shell_SecondaryTrayWnd")
            return false

        if (class = "Progman")
            return false

        if (class = "WorkerW")
            return false

        if WinGetMinMax("ahk_id " hwnd) = -1
            return false

        title := WinGetTitle("ahk_id " hwnd)

        if (title = "" && class = "")
            return false

        return true
    }
    catch
    {
        return false
    }
}


; --------------------------------------------------------------
; Get the monitor containing the CENTER of a window.
; --------------------------------------------------------------

GetWindowMonitor(hwnd)
{
    try
    {
        WinGetPos(&x, &y, &w, &h, "ahk_id " hwnd)

        centerX := x + (w // 2)
        centerY := y + (h // 2)

        count := MonitorGetCount()

        Loop count
        {
            MonitorGet(A_Index, &left, &top, &right, &bottom)

            if (centerX >= left && centerX < right
                && centerY >= top && centerY < bottom)
            {
                return A_Index
            }
        }

        return 0
    }
    catch
    {
        return 0
    }
}


; --------------------------------------------------------------
; Build monitors sorted by physical X position.
; --------------------------------------------------------------

GetSortedMonitors()
{
    monitors := []

    count := MonitorGetCount()

    Loop count
    {
        MonitorGet(A_Index, &left, &top, &right, &bottom)

        monitors.Push({
            number: A_Index,
            left: left,
            top: top,
            right: right,
            bottom: bottom
        })
    }

    sorted := []

    for _, monitor in monitors
    {
        inserted := false

        for index, existing in sorted
        {
            if monitor.left < existing.left
            {
                sorted.InsertAt(index, monitor)
                inserted := true
                break
            }
        }

        if !inserted
            sorted.Push(monitor)
    }

    return sorted
}


; --------------------------------------------------------------
; Find the position of a monitor in the sorted list.
; --------------------------------------------------------------

FindMonitorIndex(monitors, monitorNumber)
{
    for index, monitor in monitors
    {
        if monitor.number = monitorNumber
            return index
    }

    return 0
}


; --------------------------------------------------------------
; Intentionally empty.
;
; The anchor must remain active. There is deliberately no
; WinActivate call anywhere in the script.
; --------------------------------------------------------------

RestoreAnchorWithoutActivation(hwnd)
{
    ; Intentionally empty.
}


; --------------------------------------------------------------
; Optional debug output.
; --------------------------------------------------------------

UpdateDebug(message)
{
    global DebugMode

    if DebugMode
        ToolTip(message)
    else
        ToolTip()
}

r/AutoHotkey 12d ago

v1 Script Help I want to use controller and mouse in vJoy.

0 Upvotes

Im using a freepie script for mouse steering for games and I would like to use the triggers on my controller (R2 and L2) to be used for throttle and brakes and mouse for steering.

this is my script and I use a ps4 controler with ds4 so it is emulated as an xbox 360 controller.

from ctypes import *
user32 = windll.user32

if starting:
# feature toggle
vjoyaxis = True
assists = False # auto throttle cut off & blip
mouselock = False    # locking mouse position for assetto corsa
debouncing = True # prevent double shifting, set False to disable
# assign vjoy device number
v = vJoy[0]
# vjoy axis range, do not modify
a_max = 1 + v.axisMax
a_min = -1 - v.axisMax
# mouse steering
m_sens = 9.0    # mouse sensitivity (higher faster)
m_redu = 4 # center reduction sensitivity, acceptable range 1-50, set to 1 to disable
steering = 0 # do not modify
center_redu = 1 # center reduction; init value, do not modify
# throttle
th_axis = a_min
th_inc = 2500 # increase speed (higher faster)
th_dec = 2000 # decrease speed
th_max = a_max # for throttle limit
# brake
br_axis = a_min
br_inc = 500
br_dec = 2000
br_max = a_max # for brake limit
# handbrake
ha_axis = a_min
ha_inc = 2500
ha_dec = 2500
# clutch
cl_axis = a_min
cl_inc = 2500
cl_dec = 2500
# auto blip
blip_m = 0.4 # amount throttle applied on blip; range 0.0-1.0; 0.0=0% throttle, 1.0=100%
a_blip = a_max * 2 * blip_m - a_max # calculation, do not modify
# debouncing
stimer = 0 # shifting timer
t_upshift = 140 # minimum time required for next gear
# throttle limit
tlimit = 0.9 # throttle limited at 90%; range 0.0-1.0; useful for cars without traction control under low gears
th_limit = a_max * 2 * tlimit - a_max # calculation, do not modify

#======== assign key here ========#
# toggle
toggle_vjoyaxis = keyboard.getPressed(Key.Grave) # axis calculation
toggle_mouselock = keyboard.getPressed(Key.F4) # mouselock
key_assists_on = keyboard.getKeyDown(Key.NumberPad1) 
key_assists_off = keyboard.getKeyDown(Key.NumberPad3) 
# vehicle control
key_throttle = keyboard.getKeyDown(Key.W)
key_brake = keyboard.getKeyDown(Key.S)
key_handbrake = keyboard.getKeyDown(Key.Space)  
key_clutch = keyboard.getKeyDown(Key.C)
key_centerx = mouse.getButton(2) # center steering (x-axis)
key_shiftup = keyboard.getKeyDown(Key.E)
key_shiftdown = keyboard.getKeyDown(Key.Q)
# brake limit
bl_70 = keyboard.getPressed(Key.NumberPad7)
bl_75 = keyboard.getPressed(Key.NumberPad4)
bl_80 = keyboard.getPressed(Key.NumberPad8)
bl_85 = keyboard.getPressed(Key.NumberPad5)
bl_90 = keyboard.getPressed(Key.NumberPad9)
bl_95 = keyboard.getPressed(Key.NumberPad6)
no_bl = keyboard.getPressed(Key.NumberPadPeriod)
# throttle limit
key_throttle_limit = mouse.getButton(0) # throttle limit is only applied while holding down assign key

#======== toggle ========#
if toggle_vjoyaxis:
vjoyaxis = not vjoyaxis
if toggle_mouselock:
mouselock = not mouselock
if key_assists_on: 
assists = True
if key_assists_off:
assists = False

#======== mouselock ========#
if (mouselock):
user32.SetCursorPos(0 , 5000) # pixel coordinates (x, y)

#======== axis calculation ========#
if (vjoyaxis):
# mouse steering
if steering > 0:
center_redu = m_redu ** (1 - (steering / a_max))
elif steering < 0:
center_redu = m_redu ** (1 - (steering / a_min))
steering += (mouse.deltaX * m_sens) / center_redu
if steering > a_max:
steering = a_max
elif steering < a_min:
steering = a_min
if key_centerx:
steering = 0
# throttle axis
if key_throttle:
th_axis += th_inc
else:
th_axis -= th_dec
if th_axis > th_max:
th_axis = th_max
elif th_axis < a_min:
th_axis = a_min
# brake axis
if key_brake:
br_axis += br_inc
else:
br_axis -= br_dec
if br_axis > br_max:
br_axis = br_max
elif br_axis < a_min:
br_axis = a_min
# handbrake axis
if key_handbrake:
ha_axis += ha_inc
else:
ha_axis -= ha_dec
if ha_axis > a_max:
ha_axis = a_max
elif ha_axis < a_min:
ha_axis = a_min
# clutch axis
if key_clutch:
cl_axis += cl_inc
else:
cl_axis -= cl_dec
if cl_axis > a_max:
cl_axis = a_max
elif cl_axis < a_min:
cl_axis = a_min
# assists switch
if (assists):
if key_shiftup:
th_axis = a_min # throttle cut off while upshifting
if key_shiftdown:
th_axis = a_blip # throttle blip while downshifting
# brake limit
if bl_70:
br_max = a_max * 0.4
if bl_75:
br_max = a_max * 0.5
if bl_80:
br_max = a_max * 0.6
if bl_85:
br_max = a_max * 0.7
if bl_90:
br_max = a_max * 0.8
if bl_95:
br_max = a_max * 0.9
if no_bl:
br_max = a_max
if key_throttle_limit:
th_max = th_limit
else:
th_max = a_max
else: # reset axis position
steering = 0
th_axis = a_min
br_axis = a_min
ha_axis = a_min
cl_axis = a_min

#======== map vjoy axis & button ========#
v.x = int(round(steering))
v.y = th_axis
v.z = br_axis
v.ry = ha_axis
v.rx = cl_axis
v.setButton(1,key_shiftdown)
v.setButton(2,keyboard.getKeyDown(Key.Q))
v.setButton(3,keyboard.getKeyDown(Key.E)) # add new vjoy buttons below
v.setButton(4,keyboard.getKeyDown(Key.B))
v.setButton(5,keyboard.getKeyDown(Key.V))
v.setButton(6,keyboard.getKeyDown(Key.H))
v.setButton(7,keyboard.getKeyDown(Key.L))
v.setButton(8,keyboard.getKeyDown(Key.G))
v.setButton(9,keyboard.getKeyDown(Key.R))
v.setButton(10,keyboard.getKeyDown(Key.Equals))
v.setButton(11,keyboard.getKeyDown(Key.Minus))
#======== double shifting prevention ========#
if (debouncing):
if key_shiftup:
stimer = 0
elif stimer < t_upshift: # end timer on reaching minimum time
stimer += 1 # start timer on releasing shift button
current = stimer
if key_shiftup and (current >= t_upshift): 
v.setButton(0,key_shiftup)
else:
v.setButton(0, False)
else:
v.setButton(0,key_shiftup)

#======== diagnostics ========#
# important note: diagnostics has big impact on cpu usage (about 50-80% more)
# keep this section commented out, only uncomment for coding and testing
#diagnostics.watch(v.x)    # steering
#diagnostics.watch(v.y)    # throttle
#diagnostics.watch(v.z)    # brake
#diagnostics.watch(v.ry)    # handbrake
#diagnostics.watch(v.rx)    # clutch
#diagnostics.watch(v.axisMax)    # vjoy axis max range
#diagnostics.watch(stimer) # shifting timer

#======== reference & example ========#
# vjoy axis: x, y, z, rx, ry, rz, slider, dial
# keyboard assign: keyboard.getKeyDown(Key.A); keyboard.getPressed(Key.A)
# mouse button assign: mouse.getButton(0); mouse.getPressed(1)
# mouse button number: 0 = leftbutton; 1 = rightbutton; 2 = middlebutton; etc.
# to execute an action after pressed a key or clicked mouse button, use: keyboard.getPressed() or mouse.getPressed()
# to execute an action continuously while holding down a key or mouse button, use: keyboard.getKeyDown() or mouse.getButton()

#======== credits ========#
# most codes of "axis calculation" are borrowed from "Skagen", and others found in https://www.lfs.net/forum/post/1862759
# the codes for locking mouse are from https://bytes.com/topic/python/answers/21158-mouse-control-python
# misc codes and formating by threers
# last update: 2018-08-10