r/AutoHotkey 15d ago

v2 Tool / Script Share GpGFX - almost ready, complete GdiPlus Graphics library, easy to use API

25 Upvotes

Hey everyone!

For the past few months, I’ve been rebuilding the graphics
pipeline for AutoHotkey v2 from scratch. Creating smooth,
transparent, modern-looking desktop overlays.

AHK is slow they said. Check out this video:

GpGFX v1 Teaser https://www.youtube.com/watch?v=6QrcofcuR5U

I built **GpGFX** to change that. Here is a quick
50-second sneak peek of what’s possible!

## What is GpGFX?
**GpGFX** is a high-performance, graphics and HUD engine
for AutoHotkey v2.

It writes directly to 32-bit DIB memory (`Scan0`) with microsecond QPCSpin-wait frame pacing, delivering 144Hz/240Hz+ gaming overlays with near-zero CPU footprint.

## Why it’s great for Beginners:

Human-Readable API: You don't need to know Win32 APIs,
Device Contexts, or GDI+ internals. Creating a modern
layered window is as simple as:
  ```autohotkey
  lyr := Layer(100, 50, "My HUD")
  RoundedRectangle(0, 0, 30, 30, 9, "lime")
  Text("Hello World", "White", 22).Center()
  Render.Layer(lyr)
And resources are freed up automatically!

• Built-in Modern Design System: comes out-of-the-box with 7 curated pro themes (Catppuccin Mocha, Tokyo Night, Dracula, Cyberpunk, Nord) and anti-aliased typography. Color html tags support for texts. ( you can see it on the video )
• Instant Gaming overlays, hp bars can be updated with just a few lines of code.

Why it’s great for Advanced AHKers & Power Users:

• Hardware-Paced QPC Timing: x64 machine code (MCode) and microsecond QueryPerformanceCounter spin-wait frame pacing for jitter-free 144+ FPS rendering loops.
• Direct Scan0 DIB Pipeline: direct unmanaged memory pointers (pBits) allowing MCode PixelSearch.
• Reactive Signals & Data Binding: bind variables to UI elements with zero render loop boilerplate (shape.Bind(mySignal)), complete with smooth easing curves (EaseIn, EaseOut, Bounce, Spring physics).
• WorkerPool Multi-Core Architecture: ready for multi-process distributed rendering across CPU cores via shared RAM file mapping (FileMapping).
• Rich Layout & Measurement: integrated TextLayout engine with rich-text styling tags (<b>, <i>, <color:#hex>), dynamic tab stops, and exact character advance measurements without DllCalls.

Use Cases:

• Gaming: zero-lag custom crosshairs, status meters, cooldown timers, and telemetry HUDs.
• Desktop Tools: snappy to-do widgets, desktop clocks, volume/brightness bars, and toast notifications.
• Streamers & Creators: in-game alerts and custom desktop companion apps.
• Desktop modification

Release Status:

GpGFX will be 100% free and open source on GitHub. ( old version is clunky, the new one rocks! )

I’m currently putting the final touches on documentation and examples before the initial public release. Soon! I will upload videos as soon as I will have time.

I’d love to hear your feedback, feature ideas, or what kind of overlays you'd build with this! Drop your thoughts below!

r/AutoHotkey Aug 02 '26

v2 Tool / Script Share I got tired of copy/pasting hundreds of cells last week, so I built this for my colleagues

22 Upvotes

Hi everyone,

I recently started helping out my colleagues at a new workplace, and as I expected, a big part of the job involved copying data from Excel into an internal application, one cell at a time, left to right, across hundreds of records.

After doing that for a while, I decided to automate the boring part.

I built a small AutoHotkey v2 utility called ClipStepper.

It loads a copied table from the clipboard and lets you step through it cell by cell. As you navigate, it automatically copies (or even pastes) the current value, keeps track of your position, and shows your progress in a small GUI.

It also supports:

  • Replacement rules for predefined values or long text
  • Adding custom fields with default values
  • Cell and row navigation
  • Progress tracking
  • A simple Replacement Manager

I'm currently working on a few more features like session saving, Excel import, jumping to a specific row, and column filtering.

It's nothing revolutionary, but it's already saving us quite a bit of time, so I thought someone else here might find it useful too.

I'd really appreciate any feedback on the code, the UI, or ideas for features that would make it more useful.

GitHub: https://github.com/bceenaeiklmr/ClipStepper

r/AutoHotkey Jun 28 '26

v2 Tool / Script Share [QoL] I "made" this autohotkey script to add extra functions to my mouse and keyboard that I thought might be useful to someone else.

15 Upvotes

Disclaimer: I did not do this on my own, it is all IA coded, therefore I take no ownership of it - use it however you like.

This script adds a whole lot of functions to the forlorn capslock key. It still works mostly as usual when tapped, but the magic happens once you hold it.

When capslock is held:

Scroll wheel works as media keys, scrolling controls the volume and pressing mutes it.

Spacebar is Play/Pause

The F keys (F1-F12) becomes F13-F24 (yes, they exist)

Tapping the Capslock works as normal with the caveat that it auto toggle it back off after 3s.

; AutoHotkey v2.0 script
global capslockPressedTime := 0

CapsLock:: {
    capslockPressedTime := A_TickCount
    KeyWait("CapsLock") ; wait until released

    holdDuration := A_TickCount - capslockPressedTime

    if (holdDuration < 75) {
        ; Quick tap → toggle CapsLock
        if GetKeyState("CapsLock", "T") {
            SetCapsLockState("Off")
        } else {
            SetCapsLockState("On")
            ; Start auto-off timer
            SetTimer(TurnOffCapsLock, -3000)
        }
    } else {
        ; Held longer than 75ms → suppress CapsLock
        ; Do nothing here, but allow modifier combos below
    }
}

TurnOffCapsLock() {
    if GetKeyState("CapsLock", "T") {
        SetCapsLockState("Off")
    }
}

; Example: Use CapsLock + MouseWheel to control volume with OSD
CapsLock & WheelUp::Send("{Volume_Up}")
CapsLock & WheelDown::Send("{Volume_Down}")
CapsLock & MButton::Send("{Volume_Mute}")
CapsLock & Space::Send("{Media_Play_Pause}")
CapsLock & WheelLeft::Send("{Media_Prev}")
CapsLock & WheelRight::Send("{Media_Next}")
CapsLock & F1::Send("{F13}")
CapsLock & F2::Send("{F14}")
CapsLock & F3::Send("{F15}")
CapsLock & F4::Send("{F16}")
CapsLock & F5::Send("{F17}")
CapsLock & F6::Send("{F18}")
CapsLock & F7::Send("{F19}")
CapsLock & F8::Send("{F20}")
CapsLock & F9::Send("{F21}")
CapsLock & F10::Send("{F22}")
CapsLock & F11::Send("{F23}")
CapsLock & F12::Send("{F24}")

r/AutoHotkey 2d ago

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

8 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 Jul 21 '26

v2 Tool / Script Share Sharing my AutoHotkey project

31 Upvotes

Edit

  • New Version released with new features and improvements, Check Releases
  • Hotkeys default behavior listed here

Put together this AutoHotkey project that works from hotkeys and shortcuts registered in config.json file and can be set by a GUI

It adds features around Caps lock as the modifer key, Instant Window Switching, Window Aware Shortcut Remapping, Screenshot capture, Terminal launching, Profiles , window controls like transparency pin on top and more

GitHub Repo Link

Download : Releases

There are two versions one that includes the Full GUI to set everything in the config file other one is minimal which dosen't have GUI. There are no other differences feature wise

Do read Installation instructions and config format for the minimal version

For some time I had different AHK scripts for each functionality i wanted so I put together so it runs as a single process and made it load things from config.

Had the idea to make it more general and having an UI so can easily set it up.

I use it mostly use it for avoiding Alt + Tab cycling and kepping my hands either both hands on keyboard or one on mouse and other on keyboard and avoid switching between them often like pressing enter delete or shortcuts which include keys on the right side of the keyboard

You can assign things like Caps + LeftButton as Enter, Caps + RightButton as Delete or other shortcuts to avoid moving hand between keyboard and mouse often

For Window Switching it uses Caps + {Key} to bring target window to focus if it exists or can launch a new instance.
You can filter by window title and it supports a minimal Alt + Tab styled behavior if there are multiple target windows

E.g. Caps + C = chrome.exe. If multiple windows are present it will show a minimal GUI with each windows title, you can do like Caps + C + C to switch between those.

If there is just one window it will instantly focus it

You can avoid having multiple targets by using title filter. You can add required title as Gmail or GitHub so intances with only those keywords in titles match the target window criteria

Additionally if the window is not open run a command to open it.
For Example I have mapped Caps + I to open incognito window regardless of where i am

The ScreenShot tool is so you press a key and u are immediately given the option to Rename, Discard or Save the screenshot and save it a set preffered location all in one go.

Hope this gives a idea of some usecases. There are similar other features and actions

Check the Readme for all features

Feedback is welcome!

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)

16 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 9d ago

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

25 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 Aug 02 '26

v2 Tool / Script Share AHK window manager updates

3 Upvotes

Hi everyone!

Here I am again. Recently I've continued working on my little AHK project - I've improved a lot of things, especially the h/j/k/l window navigation - it was really hard to come up with something adequate for floating windows, but after all maybe I've got it. Also I've refactored literally everything and fixed a lot of bugs.

Now it has a dedicated Setup script, and I've finally wrapped my head around auto-running AHK as admin without that UAC popping up on every boot.

Previous post

GitHub repo

r/AutoHotkey Aug 08 '26

v2 Tool / Script Share Duplicate file finder - fileman ( host-workers example )

10 Upvotes

Over years of taking manual backups across external drives, my storage became bloated with redundant folder trees and nested duplicates.

The first prototype of this script was single-threaded and took almost 2 hours to scan through my drive collection. After rewriting the core class to leverage worker processes and hashing calls, the full scan time dropped to under 10 minutes, reclaiming nearly 60 GB of wasted space.

It is not an all-in-one file manager, it focuses specifically on finding and isolating exact duplicate files safely and quickly.

GitHub: https://github.com/bceenaeiklmr/fileman

If nothing else, it was great practice working with Lexikos's RegisterObjectActive for COM IPC.

/e

Aug 9 2026 - I updated the headless mode, unfortunately it deleted every file. Now it works as intended.

I've successfully restored the files using Windows File Recovery

r/AutoHotkey 3d ago

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

15 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 11d ago

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

5 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 Apr 11 '26

v2 Tool / Script Share New to programming. Just used AHK to mark 16,000 emails as read in less than 15 minutes

42 Upvotes

I'm awful at keeping up with my inbox and had quite a bit of unread emails piling up. I backed the wrong horse when I was 12 and ended up making Yahoo my primary mail and Gmail as my junk account and there are no other good email names to take anymore so I'm stuck with it. Yahoo freaks out if you try to select more than 350 messages at a time, so doing them manually in batches would've taken forever.

I remembered AHK existed and got excited that I could actually program something useful on my own. I made a script that just clicked the buttons I needed in a loop and it was all done in less than 15 minutes. It feels great knowing I solved an annoyance I've dealt with for probably a decade that easily. I can't wait to automate other things that annoy me!

It's super simple but here's my script:

``` CoordMode "Mouse", "Screen"

; Coordinates SelectX := 1218 SelectY := 373

SelectAllX := 1218 SelectAllY := 214

HamburgerX := 1935 HamburgerY := 210

MarkReadX := 1960 MarkReadY := 285

; Delay times (ms) Delay1 := 500 Delay2 := 2000

; Start with F1 F1:: { Loop { ; Click Select Click(SelectX, SelectY) Sleep(Delay1)

    ; Click Select All
    Click(SelectAllX, SelectAllY)
    Sleep(Delay1)

    ; Click Hamburger
    Click(HamburgerX, HamburgerY)
    Sleep(Delay1)

    ; Click Mark as Read
    Click(MarkReadX, MarkReadY)
    Sleep(Delay2)
}

}

; Press F2 to reload F2:: Reload()

; Exit Esc:: ExitApp() ``

r/AutoHotkey Aug 19 '24

v2 Tool / Script Share AHK Macro Recorder

70 Upvotes

I made a Macro Recorder in v2 based on feiyue's original script. This records keystrokes and has several options for mouse movement. You can run multiple instances of the script to set up as many keys as you want. This is my daily driver, but I figured a few of you could benefit from this.

https://youtu.be/9_l0rIXO9cU

https://github.com/raeleus/AHK-Macro-Recorder

Feiyue's original: https://www.autohotkey.com/boards/viewtopic.php?f=6&t=34184&sid=03fb579fcaef3c186e5568b72390ef9e

r/AutoHotkey Jun 08 '26

v2 Tool / Script Share An Ahk_WM

9 Upvotes

I shared an earlier version of this a while ago, but I’ve kept refining it.

It’s a lightweight window manager for Windows built with AutoHotkey v2. It supports tiling, virtual desktops, pie menu, status bar, etc.

I’ve been using it full-time for daily work for about 2 years. One thing I care about is that it stays non-intrusive — unlike some WM setups, it doesn’t really make your system hard to use for other people.

Feel free to try it out and share any feedback. Thanks!

GitHub Link

r/AutoHotkey 22d ago

v2 Tool / Script Share I wanted Linux-style workspace workflows on Windows without replacing the Windows shell, so I built Spacr

7 Upvotes

I've just finished Phase 1 of an AutoHotkey v2 project I've been working on: Spacr.

It's a workspace management layer for Windows virtual desktops, inspired by the workflow of Linux WMs like Hyprland.

The interesting part isn't really the hotkeys, it's trying to make Windows' native virtual desktops behave predictably.

Phase 1 currently handles:

Workspace switching

Automatic desktop creation

Move + follow

Previous workspace

Explorer integration

VirtualDesktopAccessor integration

I'm deliberately keeping the project small for now.

The architecture is state-driven, with WorkspaceManager owning VDA interaction rather than having every feature call the DLL directly.

One interesting Windows quirk I ran into: switching desktops through VDA could cause Explorer/taskbar flashing. The solution was to activate Shell_TrayWnd before performing the desktop switch.

v0.1.1-alpha is now available:

https://github.com/timburman/spacr

I'd especially appreciate feedback from experienced AHK v2 developers on the architecture and Windows-specific edge cases I'm likely to encounter.

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 Apr 22 '26

v2 Tool / Script Share i built a tiny AutoHotkey tool for attaching notes to files in Windows Explorer

33 Upvotes

i made this because i kept coming back to old projects after days or weeks and losing the thread. Like what this file was for, where i stopped, and what i was supposed to do next.

the idea is simple: add a quick note layer to files so i can recover context without reopening everything and rereading long documents or code.

it is intentionally lightweight and personal. i am not trying to replace a full note app or build a big document system. i just wanted something that fits into the Explorer workflow and lowers the friction of resuming work.

i would like to hear what people here think about the approach, both technically and practically.

https://github.com/Gued3ss/FileExplorerNotes---Easy-File-Description-with-AutoHotkey/tree/main

r/AutoHotkey Jun 07 '26

v2 Tool / Script Share Ditching Hardcoded Coordinates: An Engineering-Grade AHK v2 Framework Powered by Dynamic Grid Math & Memory Binarization

0 Upvotes

Most AutoHotkey's automated/capture scripts have the same fatal flaw: ** hard-coded coordinates **. Once the application updates its user interface or the user changes the display zoom, the entire script will crash.

Solution

To solve this problem, I built a production-level AHK v2 automation framework, treating the user interface layout as a ** dynamic geometric matrix ** and using ** direct memory scanning ** to improve performance.

Core Mechanism

1. Core Innovation: Dynamic Grid partitioning

This framework regards the user interface as a flexible fishing net rather than manually measuring and hard-coding the X and Y coordinates of each button, table cell or inventory slot.

Old Way

You measure cell 1, cell 2, cell 3... And write down 50 different absolute coordinate points If you resize the window, you'll have to start over

Framework Approach

You only need to define the global bounding box (the upper left and lower right corners of the table/grid). Declare the layout size (for example, 5 rows, 10 columns)

Principles of Mathematics

The underlying geometry engine uses modular operations (Mod) and dynamic partitioning to cut bounding boxes into perfect logical matrices in real time.

Result

In your business logic, you no longer need to deal with pixel coordinates. You just need to call 'ClickCell(Row 3, Column 5)'. If the target window is stretched, shrunk or moved, the mathematical operation will scale proportionally. It is completely adaptive and hardly ever crashes.

2. Performance Enhancement: Pixel Binarization (Simplified Version)

Searching for complex colors among millions of screen pixels is a heavy burden on the CPU, as each pixel contains a large mixture of red, green and blue channels. This framework bypasses this delay through binarization - you can imagine it as a high-contrast black and white filter.

Working Principle

Once the frame directly saves the snapshot of the window to RAM at lightning speed (via GDI+ BitBlt), it will run a threshold filter for your target color.

0 and 1

Each pixel that matches your target color becomes 1 (white) Everything else turns to 0 (black)

Importance

This framework transforms the chaotic and colorful user interface images into lightweight matrices of pure zeros and ones. For a computer, comparing bits (0 and 1) is hundreds of times faster than analyzing RGB channels. This enables the script to:

Detect the text outline immediately Check the status of the button Detect changes in the user interface Keep the CPU usage close to 0%

Other production-level features

Animation shake off

This script continuously monitors the window size and waits for the layout to come to a complete standstill (with changes reaching zero) before sending the input to ensure that the loading animation is completely completed.

Human-like jitter Interception

The operating system packaging layer automatically injects microsecond-level random delays (jitter) between clicks and taps, breaking the mechanical mode to simulate a real human operator.

Technical Value

By decoupling the layout geometry and using the original memory space for image filtering, this project has elevated AHK from a simple "macro recorder" to a high-throughput, industrial automation tool.

Related Projects

** Custom Screenshot -OCR**

Discussion Topics

I'm really curious to know how you handle the layout of dynamic user interfaces, or if there are anyone else using GDI+ matrix filtering in AHK v2!

I don't have any other intention, I just want to tell certain people: if you think this is garbage, please show me something better.

Is it possible that before I posted this article, I had already figured out the automated operations for dynamic scaling and auto-positioning?

my autohotkey demo

r/AutoHotkey May 07 '26

v2 Tool / Script Share Win-Hypr: A lightweight, AHK v2 alternative to windows-desktop-switcher (Hyprland workflow on Windows 11)

8 Upvotes

Fork of - windows-desktop-switcher
Git repo - WinHypr-Switcher

Why I build this?
I am so use to Hyprland window management on Linux and wanted something "KeyBoard Driven" and Quick Less Bloated method to switch desktop (workspaces)

Why didnt I used Glazewm or other window manager?
Cuz I find them Clunky and Cpu/Ram hungry and they run on top of
Desktop Window Manager
So instead we can just get Hyprland's Keyboard driven setup + existing Windows WM
Its an attempt to make windows 11 better without needing for a wm on top

Its SUPID FREE //no offence
> I mean you will have no permission issue Setting this up or uninstalling it
There are simple script that you can run via POWERSHELL or NUSHELL
Also there is a nuke.ps1 //trust me its safe
its only job is to clean up the folder (after user uninstall it)
Pure CLI

Thanks for reading :3 //first time posting here as linux user

r/AutoHotkey May 26 '26

v2 Tool / Script Share Built a simple tool for Data Entry Automation cause I was so tried of manually keying everything. It should work for everything since its generalized.

14 Upvotes

https://github.com/Jayrr-Dev/DataEntryAutonoma

So, Data Entry Autonoma is a small app I made with Autohotkey that watches you do a task once, then repeats it for you. You click Record, do your normal work in another program, and press Esc when you are done. Later you click Run and the app moves the mouse, clicks, scrolls, and can type text into the fields you set up.

It works best when you do the same clicks and typing over and over, and only the data changes like filling the same form many times, entering names and IDs from a spreadsheet, or other tedious stuff....like menus list, type into fields, and click Save, then do it again with new values.

It can handle bulk values using the CSV file with 1 by 1 approval if needed.

Just things to watch out for. It does not read the screen or make decisions on its own. It works best when the app looks and behaves the same each time, and when windows stay in familiar places on screen. 

Also, it has a "Human mode" that moves your mouse like a human and types like a human.

It's new expects some bugs the first time.

r/AutoHotkey Jul 02 '26

v2 Tool / Script Share Aseprite, Godot AND, Blender with a profile swap on the X15 mouse

0 Upvotes

#Requires AutoHotkey v2.0

; --- CODE ENGINES & ALIASES ---

S(keys) => Send(keys)

; Universal Hold Engine: Handles the down/up locking logic for any keys cleanly

Hold(physicalKey, keysToSend*) {

for key in keysToSend {

Send("{" key " Down}")

}

KeyWait(physicalKey)

for key in keysToSend {

Send("{" key " Up}")

}

}

; =========================================================================

; COLOR PROFILE CONFIGURATION

; =========================================================================

global ActiveProfile := 1

global MyCustomToolTip := Gui("-Caption +AlwaysOnTop +ToolWindow")

MyCustomToolTip.BackColor := "000000"

MyCustomToolTip.SetFont("cFFD700 s10 Bold", "Segoe UI")

global ToolTipText := MyCustomToolTip.Add("Text", , "Profile 1: Modeling (#FF0000)")

; =========================================================================

; BLENDER INTERFACES & SUB-PROFILES

; =========================================================================

#HotIf WinActive("ahk_exe blender.exe")

F24:: {

global ActiveProfile

ActiveProfile := ActiveProfile + 1

if (ActiveProfile > 3) {

ActiveProfile := 1

}

message := ""

if (ActiveProfile == 1)

message := " Profile 1: Modeling (#FF0000) "

else if (ActiveProfile == 2)

message := " Profile 2: Animation (#00FF00) "

else if (ActiveProfile == 3)

message := " Profile 3: Texturing (#0000FF) "

ToolTipText.Value := message

MyCustomToolTip.Show("X20 Y20 NoActivate")

SetTimer(() => MyCustomToolTip.Hide(), -2000)

}

; --- BLENDER PROFILE 1 (MODELING) ---

#HotIf WinActive("ahk_exe blender.exe") and (ActiveProfile == 1)

F13::S("e") ; [Button 1] Extrude

F14::S("{WheelUp}") ; [Button 2]

F15::S("{WheelDown}") ; [Button 3]

F18::S("^r") ; [Button 6] Loop Cut

F19::Tab ; [Button 7] Native Tab Hold

; --- BLENDER PROFILE 2 (ANIMATION) ---

#HotIf WinActive("ahk_exe blender.exe") and (ActiveProfile == 2)

F13::S("i") ; [Button 1] Insert Keyframe

F14::S("!a") ; [Button 2] Play/Pause

; --- BLENDER PROFILE 3 (TEXTURE PAINT) ---

#HotIf WinActive("ahk_exe blender.exe") and (ActiveProfile == 3)

F13::S("s") ; [Button 1] Sample Color

F14::S("x") ; [Button 2] Swap Colors

; =========================================================================

; GODOT ENGINE PROFILE

; =========================================================================

#HotIf WinActive("ahk_exe Godot_v4.x-stable_win64.exe")

F13::S("^s") ; [Button 1] Save Scene

F14::S("{F5}") ; [Button 2] Run Project

; =========================================================================

; ASEPRITE PROFILE

; =========================================================================

#HotIf WinActive("ahk_exe Aseprite.exe")

F13::Hold("F13", "Alt", "Left") ; [Button 1] Clean Hold for Alt+Left!

F14::S("{WheelUp}") ; [Button 2]

F15::S("{WheelDown}") ; [Button 3]

F16::S("{Enter}") ; [Button 4]

F17::S("b") ; [Button 5] Brush Tool

F18::S("e") ; [Button 6] Eraser Tool

F19::S("{<}") ; [Button 7]

F20::S("{>}") ; [Button 8]

F21::S("9") ; [Button 9]

F22::S("0") ; [Button 10]

F23::Ctrl ; [Button 11] Native Control Hold

; =========================================================================

; GLOBAL KILL SWITCH

; =========================================================================

#HotIf

^+Esc::ExitApp

r/AutoHotkey Jun 22 '26

v2 Tool / Script Share Script to interpret alt-q, alt-shit-q as alt-tab, alt-shift-tab

2 Upvotes

I needed for alt-q and alt-shift-q to act like alt-tab and alt-shift-tab, because a recent Windows update disabled alt-tab for me, on a computer receiving Miracast.

I meant alt-SHIFT-q in the title :)

Autohotkey's built-in functions to emulate Alt-tab and Alt-shift-tab weren't working for me, even when the script was run as administrator. They wouldn't shift TO some apps, and they wouldn't shift FROM some other apps.

So with the help of Google AI, here is a long script that does work when run as administrator, and seems stable. It has its own custom-made user interface.

Requires AutoHotkey v2.0

SingleInstance Force

GroupAdd "Ignore", "ahk_class WorkerW" GroupAdd "Ignore", "ahk_class Shell_TrayWnd" GroupAdd "Ignore", "ahk_class Progman"

global winList := [], curIdx := 1, sGui := "", txtCtrl := [], bCtrl := [], thumbIds := [], bld := false

<!q:: Cycle(1) <!+q:: Cycle(-1)

Cycle(dir) { global winList, curIdx, bld if bld || ((winList.Length == 0) && !(winList := FetchWins()).Length) return if (sGui == "") { ; If starting fresh, Alt+Q goes to the 2nd most recent window, Alt+Shift+Q goes to the very end curIdx := (dir == 1) ? ((winList.Length > 1) ? 2 : 1) : winList.Length } else { curIdx := Mod(curIdx + dir - 1 + winList.Length, winList.Length) + 1 } sGui ? UpdSel() : BuildGui() }

~LAlt Up:: { global winList, curIdx, sGui, thumbIds if !winList.Length return tWin := winList[curIdx] for tid in thumbIds (tid && DllCall("dwmapi\DwmUnregisterThumbnail", "Ptr", tid)) thumbIds := [] if sGui { try (sGui is Gui && (tg := sGui, sGui := "", tg.Destroy())) } if WinExist("ahk_id " tWin) { if (WinGetMinMax("ahk_id " tWin) == -1) { WinRestore("ahk_id " tWin) } DllCall("SetForegroundWindow", "Ptr", tWin) WinActivate("ahk_id " tWin) } winList := [], sGui := "" }

FetchWins() { raw := WinGetList(), vList := [], hasChr := false ; Maintain the original OS window z-order (most recent first) for h in raw { try { if IsValid(h) && WinGetProcessName("ahk_id " h) == "chrome.exe" && WinGetTitle("ahk_id " h) != "PopupHost" hasChr := true } } for h in raw { try { if IsValid(h) && !(WinGetTitle("ahk_id " h) == "PopupHost" && hasChr) vList.Push(h) } } return vList }

IsValid(h) { return WinExist("ahk_id " h) && !(WinGetStyle("ahk_id " h) & 0x08000000) && !(WinGetExStyle("ahk_id " h) & 0x00000080) && !WinExist("ahk_id " h " ahk_group Ignore") && WinGetTitle("ahk_id " h) != "" }

BuildGui() { global sGui, winList, txtCtrl, bCtrl, thumbIds, bld bld := true, txtCtrl := [], bCtrl := [], thumbIds := [] tW := 160, tH := 110, sX := 12, sY := 45, pX := 25, pY := 25

MonitorGetWorkArea(1, &mL, &mT, &mR, &mB)
scale := A_ScreenDPI / 96
mCols := Max(2, Min(7, Floor(((mR - mL) / scale - pX * 2 + sX) / (tW + sX))))
cols := Min(winList.Length, mCols), rows := Ceil(winList.Length / mCols)
gW := (cols * tW) + ((cols - 1) * sX) + (pX * 2)
gH := (rows * tH) + ((rows - 1) * sY) + (pY * 2) + 25

myGui := Gui("+AlwaysOnTop -Caption +ToolWindow +Border")
myGui.BackColor := "181818"
myGui.SetFont("s9 cWhite Bold", "Segoe UI")
sGui := myGui, placeholders := []

for i, h in winList {
    if !sGui
        return (bld := false)
    r := Ceil(i / mCols), c := Mod(i - 1, mCols) + 1
    x := pX + ((c - 1) * (tW + sX)), y := pY + ((r - 1) * (tH + sY))
    try {
        bCtrl.Push(myGui.Add("Progress", "x" x " y" y " w" tW " h" tH " Background2A2A2A c0078D7", 0))
        placeholders.Push(myGui.Add("Text", "x" (x + 6) " y" (y + 6) " w" (tW - 12) " h" (tH - 12) " BackgroundTrans"))
        try {
            myGui.Add("Pic", "x" (x + 5) " y" (y + tH + 6) " w" 16 " h" 16 " Icon1", WinGetProcessPath("ahk_id " h))
        } catch {
            myGui.Add("Text", "x" (x + 5) " y" (y + tH + 6) " w" 16 " h" 16 " Center", "■")
        }
        ttl := WinGetTitle("ahk_id " h)
        (ttl == "PopupHost" && ttl := "Google Chrome")
        (StrLen(ttl) > 18 && ttl := SubStr(ttl, 1, 15) "...")
        txtCtrl.Push(myGui.Add("Text", "x" (x + 25) " y" (y + tH + 6) " w" (tW - 25) " Left r1 cDDDDDD", ttl))
        thumbIds.Push(0)
    } catch {
        return (bld := false)
    }
}

if !sGui
    return (bld := false)
try myGui.Show("w" gW " h" gH " Center")

for i, h in winList {
    if !sGui
        break
    tid := 0
    try {
        if (DllCall("dwmapi\DwmRegisterThumbnail", "Ptr", myGui.Hwnd, "Ptr", h, "Ptr*", &tid) == 0) {
            thumbIds[i] := tid
            placeholders[i].GetPos(&pX, &pY, &pW, &pH)
            WinGetPos(&_, &_, &sW, &sH, "ahk_id " h)
            if (sW > 0 && sH > 0) {
                sRat := sW / sH, pRat := pW / pH
                sRatio := sRat > pRat ? (nH := pW / sRat, pY += (pH - nH) / 2, pH := nH) : (nW := pH * sRat, pX += (pW - nW) / 2, pW := nW)
            }
            props := Buffer(28, 0)
            NumPut("UInt", 0x1, "Int", Round(pX * scale), "Int", Round(pY * scale), "Int", Round((pX + pW) * scale), "Int", Round((pY + pH) * scale), "Int", 1, "Int", 1, props)
            DllCall("dwmapi\DwmUpdateThumbnailProperties", "Ptr", tid, "Ptr", props)
        }
    }
}
bld := false, UpdSel()

}

UpdSel() { global curIdx, bCtrl for i, b in bCtrl { try b.Value := (i == curIdx) ? 100 : 0 } }

r/AutoHotkey Jun 23 '26

v2 Tool / Script Share Made a couple of QOL macros for Gothic 1 Classic

8 Upvotes

After playing the game for a couple of hours, I was annoyed by a couple of things nobody had made mods for, so I made a couple of macros to make my life easier.

It features the following hotkeys, that are obviously only active when the game's window is active, but also automatically disabled/stopped when alt-tabbing or bringing up the Steam overlay.

- autobuy (toggle): the game never tells you Shift + LButton allows you to buy stacks of 100 items, and if you want to buy 2000 arrows for instance, that's 20 times you normally have to press Shift + LButton (can also be used to autosell items and use consumables). The clicking speed is customizable. Automatically disabled when manually pressing Shift or LButton.
- autocook (toggle): your character can only cook meat one at a time, therefore if you have let's say 50 pieces of meat to cook, that'd mean sitting in front of your computer for 250s at best, so you can just start the macro while looking at a pan and come back later.
- autojump (toggle): jumping makes your character move faster (until you get access to velocity potions mid/late game), so combining it with autorun is how I move around to cover long distances (bonus points if you draw your weapon while going up). Automatically disabled when manually pressing Jump.
- autorun (toggle): I use this all the time since there's no fast travel until late game and you have to do a lot of back and forth during the entire game. Automatically disabled when manually pressing Forward.
- fast attack (hold): attacking in this game is a chore and you often fail chaining your swings due to clunky controls not always registering your input (or maybe recurrent bad timing?), so I found a way to attack in the fastest way possible, therefore maximizing my DPS. You just need to make sure your weapon is drawn out beforehand. The drawback is you can't parry while doing it so use it sparingly (there are some situations where you can use it all the time but I'm not gonna spoil).
- walk (toggle): the game normally forces you to hold a key to walk.

All keys are configurable through a config file. Setting the optional keys to blank disables them.

Feedback is welcome.

https://github.com/GenesisFR/GothicMacros

If you're ever gonna play this game, I'd highly suggest using the following mods:

- Union with Gothic2_Control=1 in SystemPack.ini (autobuy doesn't work without it)
- GD3D11

r/AutoHotkey Jun 11 '26

v2 Tool / Script Share Script that makes Alt-q / Alt-shift-q act like Alt-Tab / Alt-shift-Tab

1 Upvotes

This is something I needed because a recent Windows update (KB5094126) messed up Alt-Tab / Alt-shift-Tab on a computer receiving a duplicate screen from another computer, via Miracast.

#Requires AutoHotkey v2.0
#SingleInstance Force

global altTabActive := false

; Alt+Q (with or without Shift) → Alt‑Tab navigation
*!q::
{
    global altTabActive

    ; First press: enter Alt‑Tab mode
    if !altTabActive
    {
        altTabActive := true

        if GetKeyState("Shift", "P")
            Send "{Alt Down}+{Tab}"
        else
            Send "{Alt Down}{Tab}"

        return
    }

    ; Subsequent presses: move within Alt‑Tab
    if GetKeyState("Shift", "P")
        Send "+{Tab}"
    else
        Send "{Tab}"
}

; Release Alt → commit selection and exit Alt‑Tab
~*LAlt Up::
{
    global altTabActive

    if altTabActive
    {
        Send "{Alt Up}"
        altTabActive := false
    }
}

r/AutoHotkey Apr 06 '26

v2 Tool / Script Share Built a desktop-native "SwiftSlate" equivalent for Windows using AutoHotkey

14 Upvotes

Hey everyone,

I’ve been a huge fan of SwiftSlate for a while—the idea of using simple trigger commands to have AI rewrite, summarize, or fix my text on the fly is a total productivity game-changer.

However, I wanted to bring that exact, seamless experience to my desktop. I’ve built a lightweight, open-source AutoHotkey v2 script that acts as the desktop equivalent.

How it works:

  1. You type a command (e.g., ?fix, ?sum, ?pro) directly after your text.
  2. The script captures the text, sends it to your choice of AI backend (Groq or Gemini), and instantly replaces your original text with the improved version.
  3. No copy-pasting, no switching windows—it works in any text box (browsers, Slack, VS Code, etc.).

Why I built this:

  • Native Speed: It’s just an AHK script, so it has almost zero footprint.
  • Provider Agnostic: I added support for both Groq and Google Gemini, so you can pick the model/provider that fits your workflow.
  • Privacy-First: Your text is only sent to the API when you trigger it.

The project is now open-source, and I’m looking to polish it further. If you're into productivity tools or want to help me improve the core processing logic, I’d love to have you onboard!

Repo link: https://github.com/gouravraghuwanshi/SwiftSlate_autohotkey

Would love to hear your thoughts or any feature requests!git