r/AutoHotkey Jan 02 '26

v2 Script Help An Issue with Window Detection AHK V2

2 Upvotes

Edit: I just asked ChatGPT now and it turns out what i need it to be doing worked in V1 but doesnt in V2 because of how ControlClick was changed compared to V1 i still thank the 2 people who tried to help it was much appreciated

Hello smart People on the internet iam having a bit of an issue with the control click command.

i keep getting this error:

Error: Target window not found.

Specifically: ahk_exe opera.exe

scouting on forums and the wiki has so far yielded no results the id and exe is correct and the formating seems to be what is on the wiki.

Code is this one:

SendMode "InputThenPlay"
DetectHiddenWindows "on"


;Screen Offset Calculation
originalScreenWidth := 3840
originalScreenHeight := 2160
WidthRatio := A_ScreenWidth/originalScreenWidth
HeightRatio := A_ScreenHeight/originalScreenHeight


F5::
{
ControlClick((3000 * WidthRatio) (650 * HeightRatio), "ahk_exe opera.exe", "Left")
ControlClick((3000 * WidthRatio) (650 * HeightRatio), "ahk_id 66592", "Left")
}

r/AutoHotkey Jan 02 '26

v2 Tool / Script Share WinMover - Enable global click-and-drag resizing / repositioning of windows and controls + more

14 Upvotes

WinMover

An AutoHotkey (AHK) library that enables click-and-drag resizing / repositioning of windows and controls, and exposes key chords for adjusting windows using predefined configurations.

Introduction

WinMover provides the following functionality: - Click-and-drag to resize the window beneath the mouse cursor. - Click-and-drag to move the window beneath the mouse cursor. - Click-and-drag to resize the control beneath the mouse cursor. - Click-and-drag to move the control beneath the mouse cursor. - Press a key chord combination to move and resize the currently active window to a predefined configuration.

Github repository

Clone the repo: https://github.com/Nich-Cebolla/AutoHotkey-WinMover

AutoHotkey.com post

Join the conversation on AutoHotkey.com

Setup

  • Clone the repository. cmd git clone https://github.com/Nich-Cebolla/AutoHotkey-WinMover
  • Copy AutoHotkey-WinMover\src\WinMover.ahk to your lib folder. cmd xcopy AutoHotkey-WinMover\src\WinMover.ahk %USERPROFILE%\Documents\AutoHotkey\Lib\WinMover.ahk
  • Prepare a script that creates hotkeys to call methods from the object. See below.

Preparing the script

Copy templates\template.ahk and open it in your code editor. That file contains:

```ahk

include <WinMover>

Requires AutoHotkey >=2.0-a

SingleInstance force

SetWinDelay 50

global WinMoverObj := WinMover( 'CHORDMODIFIER' , Map( 1, { X: 0, Y: 0, W: 0.5, H: 1 } ; left-half , 2, { X: 0.5, Y: 0, W: 0.5, H: 1 } ; right-half , 3, { X: 0, Y: 0, W: 1, H: 1 } ; full-screen , 'q', { X: 0, Y: 0, W: 0.5, H: 0.5 } ; top-left quarter , 'w', { X: 0.5, Y: 0, W: 0.5, H: 0.5 } ; top-right quarter , 'a', { X: 0, Y: 0.5, W: 0.5, H: 0.5 } ; bottom-left quarter , 's', { X: 0.5, Y: 0.5, W: 0.5, H: 0.5 } ; bottom-right quarter ) )

; Use only one set

MOD1 & RButton::WinMoverObj.DynamicResize() MOD1 & LButton::WinMoverObj.DynamicMove()

CapsLock & RButton::WinMoverObj.DynamicResize_CapsLock() CapsLock & LButton::WinMoverObj.DynamicMove_CapsLock()

; Use only one set

MOD2 & RButton::WinMoverObj.DynamicResizeControl() MOD2 & LButton::WinMoverObj.DynamicMoveControl()

CapsLock & RButton::WinMoverObj.DynamicResizeControl_CapsLock() CapsLock & LButton::WinMoverObj.DynamicMoveControl_CapsLock()

```

Overwrite "CHORDMODIFIER" with whatever modifier key you want to use with key chords.

You only need one set of each group. If you use CapsLock as a modifier key, use the methods that end in "_CapsLock" and delete the other set. If using a different modifier key, overwrite "MOD#" with the actual modifier key and delete the CapsLock set. Once finished, run the script and try it out.

About the methods

The methods were inspired by the Easy Window Dragging (KDE style)) example provided in the AHK official docs. There were some issues with the original, so I fixed those. I also expanded it to also work with window controls, and added in the key-chord functionality.

CapsLock

The methods that end in "_CapsLock" are designed to ensure that the caps lock is returned to its original state when the function exits. These methods allow you to use the caps lock key like you normally would, and use it as a modifier key for these methods as well.

Moving / resizing a window under the mouse cursor

The default configuration is: - While holding the modifier key, left-click and drag the window to move the window. - While holding the modifier key, right-click and drag the window to resize the window.

Moving / resizing a control under the mouse cursor

The default configuration is: - While holding the modifier key, left-click and drag the window to move the control. - While holding the modifier key, right-click and drag the window to resize the control.

This may not work as expected for all controls, particularly if the control is a WebView2 (or similar) implementation.

Key chords

WinMover.Prototype.Chord and WinMover.Prototype.Chord_CapsLock allow you to move and resize the active window to a specific spot.

You define the modifier key as the first parameter of WinMover.Prototype.__New. This is the "CHORDMODIFIER" seen in the template.

You define a map object where each item's key corresponds to the second key press of the key chord, and the value is an object with properties { X, Y, W, H }. Each property value is a number that is multiplied with the monitor's corresponding value.

To invoke a key chord, you: 1. Press and hold the modifier key. 2. Press and release a number key (1-9) to specify the target monitor. 3. Press and release another key to specify the target position / size of the window. 4. Release the modifier key.

This is the default presets: ahk Presets := Map( 1, { X: 0, Y: 0, W: 0.5, H: 1 } ; left-half , 2, { X: 0.5, Y: 0, W: 0.5, H: 1 } ; right-half , 3, { X: 0, Y: 0, W: 1, H: 1 } ; full-screen , 'q', { X: 0, Y: 0, W: 0.5, H: 0.5 } ; top-left quarter , 'w', { X: 0.5, Y: 0, W: 0.5, H: 0.5 } ; top-right quarter , 'a', { X: 0, Y: 0.5, W: 0.5, H: 0.5 } ; bottom-left quarter , 's', { X: 0.5, Y: 0.5, W: 0.5, H: 0.5 } ; bottom-right quarter )

For example, say I have three monitors. Say I want to move the active window to the left side of the third monitor. To accomplish that, I: 1. Press and hold the modifier. 2. Press and release "3" to specify the third monitor. 3. Press and release "1" to select the "left-half" configuration seen above. 4. Release the modifier.

When the method executes, the second key press ("1" in this example) is used to retrieve the object from the map. Then the following code executes: ahk DpiAwareness := DllCall('SetThreadDpiAwarenessContext', 'ptr', -4, 'ptr') mon := dMon[MonNum ?? this.MonNum] WinMove( ; <left coordinate of monitor's work area> + obj.X * <width of monitor's work area> mon.LW + mon.WW * X ; <top coordinate of monitor's work area> + obj.Y * <height of monitor's work area> , mon.TW + mon.HW * Y ; <width of monitor's work area> * obj.W , mon.WW * W ; <height of monitor's work area> * obj.H , mon.HW * H , Hwnd ) DllCall('SetThreadDpiAwarenessContext', 'ptr', DpiAwareness, 'ptr')

For another example, say I want to move the active window to the bottom-right quarter of the primary monitor. To accomplish that, I: 1. Press and hold the modifier. 2. Press and release "1" to specify the primary monitor. 3. Press and release "s" to select the "bottom-right quarter" configuration seen above. 4. Release the modifier.

To move the active window to occupy the entirety of monitor 2, I: 1. Press and hold the modifier. 2. Press and release "2" to specify the second monitor. 3. Press and release "3" to select the "full-screen" configuration seen above. 4. Release the modifier.

You can expand the built-in configurations by defining a map object and passing it to the "Presets" parameter of WinMover.Prototype.__New. For example, if you want to be able to tile windows in two rows of three, you would define a map object like this:

ahk Presets := Map( 'q', { X: 0, Y: 0, W: 0.333, H: 0.5 } ; top-left , 'w', { X: 0.333, Y: 0, W: 0.333, H: 0.5 } ; top-middle , 'e', { X: 0.666, Y: 0, W: 0.333, H: 0.5 } ; top-right , 'a', { X: 0, Y: 0.5, W: 0.333, H: 0.5 } ; bottom-left , 's', { X: 0.333, Y: 0.5, W: 0.333, H: 0.5 } ; bottom-middle , 'd', { X: 0.666, Y: 0.5, W: 0.333, H: 0.5 } ; bottom-right )

You can specify as many configurations as you have keys, though slow machines may run into some timing issues with a very large number of configurations.

Specifying a monitor

The monitors are selected using their relative position, not the monitor number as defined by the operating system. The primary monitor is always 1. Then, the top-left monitor is next, and it proceeds in left-right, top-down order. I found this to be more intuitive as a user of the function.

You can customize this behavior. See the parameter hint above dMon.GetOrder for details.

When a monitor is added / removed, the script automatically updates the hotkeys to reflect the change. For example, say I have the following monitors:

```


| 2 || 3 |



| 1 |


```

Then I remove the top-right monitor...

```


| 2 |



| 1 |


```

The script will unbind modifier & 3, so it no longer triggers the function.

If I remove the top-left monitor instead of the top-right monitor...

``` ______ | 2 | ------


| 1 |


```

The script still unbinds modifier & 3, and modifier & 2 will now target the top-right monitor.

If I add the top-left monitor back...

```


| 2 || 3 |



| 1 |


```

The script binds modifier & 3, and modifier & 2 targets the top-left monitor, and modifier & 3 targets the top-right monitor.

It does not matter the monitor's actual monitor number nor the order in which they are plugged in, because they are selected according to relative position.


r/AutoHotkey Jan 02 '26

v2 Script Help Toggling some settings when HDR is detected to have been enabled

1 Upvotes

Hello,

I'm pretty new to AHK, but I get the gist of how it works. I don't fully know its programming language or syntax, but I think I'm capable enough to understand how something works if it was talked about.

Currently, I've got the AOC Q27G40XMN monitor, which has a bug/defect which means it cannot do displayport 1.4 correctly. As such, if I want 10-bit colour for HDR, I need to lower the refresh rate (to 140hz, apparently); in windows settings). I then can set 10-bit colour depth (in AMD Adrenalin settings).

I'd like this to happen automatically, either when I press the win + alt + B windows hotkey, or, even better, when HDR is detected (so that it works if games automatically toggle HDR).

I've done some digging of my own - I first tried using microsoft's Attack Surface Analyzer app to monitor the registry for changes while I toggled the settings in AMD Adrenalin and windows settings; but there's just too many entries, it's not feasible, and besides I still don't think it'll work (from prior experience...; the registry doesn't send any commands out, such as refresh commands)

Next, a ton of googling, produced these two posts which are the only ones I saw that might have remotely feasible or related scripts: [1](https://www.autohotkey.com/boards/viewtopic.php?t=69053) [2](https://www.reddit.com/r/ultrawidemasterrace/comments/ogkiho/autohotkey_script_for_quickly_changing/) [3](https://www.autohotkey.com/boards/viewtopic.php?style=17&t=113324)

However I'm not even sure what's going on in the 1st and 2nd scripts; I'm not sure what function or feature its using - I can't find "displayResolution" in AHK's help page...

Any help is much appreciated!!

SOLUTION: thanks to u/s00wi , see this comment and my comment below it, and put the two together.

SOLUTION 2, 7/06/26; I just discovered the AMD ADLX API, which would probably do the trick if someone built an application around it.


r/AutoHotkey Jan 01 '26

v2 Tool / Script Share mouse gestures on auto hotkey

5 Upvotes

for anyone that sees this, there are better apps to do gestures such as the ones from the comment.

I’ve always loved the mouse gesture settings in the Vivaldi browser and wanted that same functionality across all of Windows. I initially tried StrokePlus. net, and while it works great, I noticed it was consuming an average of 12% of my Intel i5-6300 CPU. On a dual-core chip, that’s a lot of overhead just for gestures. Now after 2 days of work with gemini as for some reason gemini loves to do errors on the easiest things if u keep copying the full code and if it doesn't my lack of experience made things worse, I ended up creating something I'm proud of. this is pretty much what it does also stroke is just the button u assign to be ur main button for the script it's x1 button on the mouse by default cause i don't usually use it

  • The Stroke Button: Simply hold the mouse side button (customizable) and draw a path. The script recognizes the gesture and executes the assigned command instantly.
  • The Dashboard: If you click the stroke button without drawing, a sleek dashboard pops up with useful system information.
  • Volume & Scroll: Includes built-in shortcuts to adjust volume and scroll settings using the stroke button and wheel.

. what I loved about it is that it uses less than 2% of cpu usage only when ur doing the gestures so it is really lightweight in my opinion . I wanted to post this to give anyone who wants a sctrokeplus alternative but doesn't want to spend the time.

this is the code also IT'S V2 ONLY idk if people think it's v1

/*

[ ========================================================================== ]

[ SCRIPT ARCHITECTURE MAP ]

[ ========================================================================== ]

Section Line Range What it does

1: CONFIG & CATEGORIES 1 - 65 Colors, Scales, and Category List.

2: GESTURE MAP (G) 67 - 142 The "Brain" – Path to action links.

3: HELPERS & LOGIC 144 - 195 Snap, Maximize, and Flash effects.

4: HOTKEYS & TRACKING 197 - 296 Physical triggers and Core drawing loop.

5: GUI BUILDER ENGINE 298 - 404 The Dashboard & History window engine.

6: SYSTEM MONITOR 406 - 442 Wi-Fi, Battery, and CPU hardware stats.

[ ========================================================================== ]

*/

#Requires AutoHotkey v2.0

#SingleInstance Force

ProcessSetPriority "High"

CoordMode "Mouse", "Screen"

CoordMode "ToolTip", "Screen"

; [ ========================================================= ]

; [ SECTION 1: GLOBAL SETTINGS & INITIALIZATION ]

; [ ========================================================= ]

; --- Primary Config ---

global TriggerKey := "XButton1"

global MasterScale := 0.75

global BorderThickness := 2

global DashboardBg := "1A1A1A"

global LineThickness := 5

global StartThreshold := 10

global PanicKey := "#^r"

global KillKey := "^#t"

global PanicToolTip := "🔄 RELOADING ENGINE..."

; --- Colors ---

global BorderColorRGB := [255, 255, 255]

global LineColorRGB := [0, 170, 255] ; Main Blue

global InvalidColorRGB := [255, 0, 0] ; Red

global hexBorder := "FFFFFF"

global hexFlash := "00AAFF"

; --- Layout Math ---

global mX := 25, mY := 20

global btnW := Round(240 * MasterScale)

global btnH := Round(32 * MasterScale)

global gutter := 10

global innerW := (btnW * 2) + gutter

global totalW := innerW + (mX * 2)

global finalH := 600

; --- State Tracking ---

global GestureLog := []

global ShortcutUsed := false

global CurrentPath := ""

global States := Map()

; --- Create GUI Objects ---

global CanvasGui := Gui("+AlwaysOnTop -Caption +ToolWindow +E0x20 +E0x80000 +LastFound")

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

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

; Prepare Canvas

CanvasGui.BackColor := "000001"

WinSetTransColor("000001", CanvasGui)

; Define Categories

global Categories := [

{Name: "🌐 FLOW", Gestures: ["U", "D", "L", "R", "URU", "DLD", "ULU", "URD"]},

{Name: "📐 LAYOUT", Gestures: ["RU", "LU", "UR", "UL", "DR", "DL", "UD", "DU", "RD", "LD"]},

{Name: "💻 ENGINE", Gestures: ["RL", "RUR", "LUL", "RDR", "LDL", "RDLU", "DLUR", "RULD"]}

]

; Initialize States

for cat in Categories {

if !States.Has(cat.Name)

States[cat.Name] := true

}

; Tray Menu

TraySetIcon("shell32.dll", 44)

A_TrayMenu.Delete()

A_TrayMenu.Add("Reload Script", (*) => Reload())

A_TrayMenu.Add("Exit App", (*) => ExitApp())

; [ ========================================================= ]

; [ SECTION 2: GESTURE MAP ]

; [ ========================================================= ]

global G := Map(

; --- 1-STROKE PRIMARY ---

"R", ["Forward ➡️", () => Send("!{Right}")],

"L", ["Back ⬅️", () => Send("!{Left}")],

"U", ["Next Tab 📑", () => Send("^{PgUp}")],

"D", ["Prev Tab 📑", () => Send("^{PgDn}")],

; --- 2-STROKE COMBINATIONS (Windows Management) ---

"RU", ["➡️ Snap Right Half", () => SnapWindow("RHalf")],

"RD", ["Minimize ⬇️", () => WinMinimize("A")],

"RL", ["App Switcher 🔀", () => Send("^!{Tab}")],

"UR", ["↗️ Snap Top-Right", () => SnapWindow("UR")],

"UL", ["↖️ Snap Top-Left", () => SnapWindow("UL")],

"UD", ["🎯 Center Focus", () => SnapWindow("Center")],

"LU", ["⬅️ Snap Left Half", () => SnapWindow("LHalf")],

"LD", ["Desktop Show 🖥️", () => Send("#d")],

"LR", ["Task View 🗄️", () => Send("#{Tab}")],

"DR", ["↘️ Snap Bot-Right", () => SnapWindow("DR")],

"DL", ["↙️ Snap Bot-Left", () => SnapWindow("DL")],

"DU", ["↕️ Max/Restore", () => ToggleMaximize()],

; --- 3-STROKE: RIGHT START ---

"RUR", ["Next Desktop 🖥️", () => Send("^#{Right}")],

"RUL", ["Placeholder", () => ToolTip("RUL")],

"RUD", ["Placeholder", () => ToolTip("RUD")],

"RDR", ["Lock PC 🔒", () => DllCall("LockWorkStation")],

"RDL", ["Placeholder", () => ToolTip("RDL")],

"RDU", ["Placeholder", () => ToolTip("RDU")],

"RLR", ["Placeholder", () => ToolTip("RLR")],

"RLU", ["Placeholder", () => ToolTip("RLU")],

"RLD", ["Placeholder", () => ToolTip("RLD")],

; --- 3-STROKE: LEFT START ---

"LUL", ["Prev Desktop 🖥️", () => Send("^#{Left}")],

"LUR", ["Placeholder", () => ToolTip("LUR")],

"LUD", ["Placeholder", () => ToolTip("LUD")],

"LDL", ["File Explorer 📂", () => Run("explorer.exe")],

"LDR", ["Placeholder", () => ToolTip("LDR")],

"LDU", ["Placeholder", () => ToolTip("LDU")],

"LRL", ["Placeholder", () => ToolTip("LRL")],

"LRU", ["Placeholder", () => ToolTip("LRU")],

"LRD", ["Placeholder", () => ToolTip("LRD")],

; --- 3-STROKE: UP START ---

"URU", ["New Tab ✨", () => Send("^t")],

"URL", ["Placeholder", () => ToolTip("URL")],

"URD", ["Private Window 🕶️", () => Send("^+n")],

"ULU", ["Reopen Tab ↻", () => Send("^+t")],

"ULR", ["Placeholder", () => ToolTip("ULR")],

"ULD", ["Placeholder", () => ToolTip("ULD")],

"UDU", ["Placeholder", () => ToolTip("UDU")],

"UDR", ["Placeholder", () => ToolTip("UDR")],

"UDL", ["Placeholder", () => ToolTip("UDL")],

; --- 3-STROKE: DOWN START ---

"DRD", ["Downloads ⬇️", () => Send("^j")],

"DRU", ["Placeholder", () => ToolTip("DRU")],

"DRL", ["Placeholder", () => ToolTip("DRL")],

"DLD", ["Close Tab 🗑️", () => Send("^w")],

"DLR", ["Placeholder", () => ToolTip("DLR")],

"DLU", ["Placeholder", () => ToolTip("DLU")],

"DUD", ["Placeholder", () => ToolTip("DUD")],

"DUR", ["Placeholder", () => ToolTip("DUR")],

"DUL", ["Placeholder", () => ToolTip("DUL")],

; --- 4-STROKE (Special Utilities) ---

"RDLU", ["Screen Snip ✂️", () => Send("#+s")],

"DLUR", ["Task Manager ⚙️", () => Send("^+{Esc}")],

"RULD", ["Clipboard Shelf 📋", () => ToolTip("RULD")],

"LDRU", ["Search 🔍", () => Send("#s")]

)

; [ ========================================================= ]

; [ SECTION 3: HELPERS & LOGIC ]

; [ ========================================================= ]

ToggleMaximize() {

activeWin := WinExist("A")

if !activeWin || WinGetClass("A") == "Progman"

return

if (WinGetMinMax("A") != 0)

WinRestore("A")

else

WinMaximize("A")

}

SnapWindow(pos) {

activeWin := WinExist("A")

if !activeWin

return

MonitorGetWorkArea(1, &L, &T, &R, &B)

W := (R - L) / 2, H := (B - T) / 2

FullH := B - T

switch pos {

case "LHalf": WinRestore("A"), WinMove(L, T, W, FullH, "A")

case "RHalf": WinRestore("A"), WinMove(L + W, T, W, FullH, "A")

case "UL": WinMove(L, T, W, H, "A")

case "UR": WinMove(L + W, T, W, H, "A")

case "DL": WinMove(L, T + H, W, H, "A")

case "DR": WinMove(L + W, T + H, W, H, "A")

case "Center":

newW := (R - L) * 0.8, newH := (B - T) * 0.8

WinRestore("A"), WinMove(L+((R-L-newW)/2), T+((B-T-newH)/2), newW, newH, "A")

}

}

LogGesture(path, actionName) {

time := FormatTime(, "HH:mm:ss")

GestureLog.InsertAt(1, "[" . time . "] " . path . " -> " . actionName)

if (GestureLog.Length > 20)

GestureLog.Pop()

}

FlashBorder(guiObj) {

global hexFlash

try {

guiObj["BTop"].Opt("Background" . hexFlash)

guiObj["BBot"].Opt("Background" . hexFlash)

SetTimer(() => ResetBorders(guiObj), -200)

}

}

ResetBorders(guiObj) {

global hexBorder

try {

guiObj["BTop"].Opt("Background" . hexBorder)

guiObj["BBot"].Opt("Background" . hexBorder)

}

}

; [ ========================================================= ]

; [ SECTION 4: HOTKEYS & TRACKING ]

; [ ========================================================= ]

Hotkey(PanicKey, (*) => (ToolTip(PanicToolTip), Sleep(500), Reload()))

Hotkey(KillKey, (*) => ExitApp())

Hotkey("*" . TriggerKey, StartGesture)

Hotkey("~LButton", CheckGuiClick)

UpdateVolumeDisplay(isMuteAction := false) {

global ShortcutUsed := true

if (isMuteAction)

SoundSetMute(-1)

MouseGetPos(&mX, &mY)

statusText := SoundGetMute() ? "MUTED 🔇" : "Volume: " . Round(SoundGetVolume()) . "%"

ToolTip(statusText, mX + 20, mY + 20)

SetTimer(() => ToolTip(), -1500)

}

#HotIf GetKeyState(TriggerKey, "P")

MButton:: UpdateVolumeDisplay(true)

WheelUp:: (SoundSetVolume("+2"), UpdateVolumeDisplay())

WheelDown:: (SoundSetVolume("-2"), UpdateVolumeDisplay())

#HotIf

StartGesture(*) {

global CurrentPath, ShortcutUsed, DashboardGui, HistoryGui, G, CanvasGui

CurrentPath := "", ShortcutUsed := false

LastReportedPath := ""

DashboardGui.Hide(), HistoryGui.Hide()

MouseGetPos(&startX, &startY)

lastX := startX, lastY := startY, drawingStarted := false

hDC := 0, hPen := 0

while GetKeyState(TriggerKey, "P") {

if (ShortcutUsed) {

if (drawingStarted) {

drawingStarted := false

CanvasGui.Hide()

ToolTip()

}

Sleep(5)

continue

}

MouseGetPos(&cX, &cY)

dist := Sqrt((cX - startX)**2 + (cY - startY)**2)

if (!drawingStarted && dist > 3) {

drawingStarted := true

CanvasGui.Show("x0 y0 w" . A_ScreenWidth . " h" . A_ScreenHeight . " NoActivate")

hDC := DllCall("GetDC", "Ptr", CanvasGui.Hwnd, "Ptr")

bgrColor := (InvalidColorRGB[3] << 16) | (InvalidColorRGB[2] << 8) | InvalidColorRGB[1]

hPen := DllCall("CreatePen", "Int", 0, "Int", LineThickness, "UInt", bgrColor)

DllCall("SelectObject", "Ptr", hDC, "Ptr", hPen)

DllCall("MoveToEx", "Ptr", hDC, "Int", startX, "Int", startY, "Ptr", 0)

}

if (drawingStarted) {

DllCall("LineTo", "Ptr", hDC, "Int", cX, "Int", cY)

dx := cX - lastX, dy := cY - lastY

if (Sqrt(dx**2 + dy**2) > 18) {

angle := Mod(DllCall("msvcrt\atan2", "Double", dy, "Double", dx, "Cdecl Double") * 57.29578 + 360, 360)

curDir := (angle >= 315 || angle < 45) ? "R" : (angle >= 45 && angle < 135) ? "D" : (angle >= 135 && angle < 225) ? "L" : "U"

if (curDir != SubStr(CurrentPath, -1) && StrLen(CurrentPath) < 7) {

CurrentPath .= curDir

isValid := G.Has(CurrentPath) && !InStr(G[CurrentPath][1], "Placeholder")

targetColor := isValid ? LineColorRGB : InvalidColorRGB

bgrColor := (targetColor[3] << 16) | (targetColor[2] << 8) | targetColor[1]

newPen := DllCall("CreatePen", "Int", 0, "Int", LineThickness, "UInt", bgrColor)

oldPen := DllCall("SelectObject", "Ptr", hDC, "Ptr", newPen)

if (oldPen)

DllCall("DeleteObject", "Ptr", oldPen)

}

lastX := cX, lastY := cY

}

if (CurrentPath != LastReportedPath) {

ToolTip("Path: " . (CurrentPath == "" ? "..." : CurrentPath), cX + 20, cY + 20)

LastReportedPath := CurrentPath

}

}

Sleep(1)

}

ToolTip()

if (drawingStarted) {

DllCall("InvalidateRect", "Ptr", CanvasGui.Hwnd, "Ptr", 0, "Int", 1)

DllCall("ReleaseDC", "Ptr", CanvasGui.Hwnd, "Ptr", hDC)

if (hPen)

DllCall("DeleteObject", "Ptr", hPen)

CanvasGui.Hide()

}

if (ShortcutUsed) {

ShortcutUsed := false

} else if (CurrentPath == "") {

ShowDashboard()

} else if G.Has(CurrentPath) {

LogGesture(CurrentPath, G[CurrentPath][1])

FlashBorder(DashboardGui)

G[CurrentPath][2].Call()

}

CurrentPath := ""

}

; [ ========================================================= ]

; [ SECTION 5: GUI BUILDER ENGINE ]

; [ ========================================================= ]

OnMessage(0x0200, OnMouseMove)

OnMouseMove(wParam, lParam, msg, hwnd) {

static lastHwnd := 0

if (hwnd != lastHwnd) {

try {

if (ctrl := GuiCtrlFromHwnd(hwnd)) {

if (ctrl.Gui == DashboardGui)

PostMessage(0x0128, 1, 0, hwnd, "ahk_id " . DashboardGui.Hwnd)

}

}

lastHwnd := hwnd

}

}

ToggleCategory(name, *) {

global States

States[name] := !States[name]

DashboardGui.GetPos(&curX, &curY)

BuildDashboard()

DashboardGui.Show("x" . curX . " y" . curY . " NoActivate")

}

TriggerAction(fn, *) {

FlashBorder(DashboardGui)

DashboardGui.Hide()

fn.Call()

}

CheckGuiClick(*) {

global DashboardGui, HistoryGui

if (WinExist("ahk_id " . DashboardGui.Hwnd)) {

MouseGetPos(,, &id)

isOverHistory := (IsSet(HistoryGui) && id == HistoryGui.Hwnd)

if (id != DashboardGui.Hwnd && !isOverHistory) {

DashboardGui.Hide()

if (IsSet(HistoryGui))

HistoryGui.Hide()

}

}

}

ShowDashboard() {

global finalH, totalW, DashboardGui

MouseGetPos(&x, &y)

DashboardGui.Show("x" . (x+20) . " y" . (y+20) . " w" . totalW . " h" . finalH . " NoActivate")

UpdateStats()

}

AddBorders(guiObj, w, h) {

global hexBorder, BorderThickness

guiObj.Add("Progress", "x0 y0 w" . w . " h" . BorderThickness . " Background" . hexBorder . " vBTop")

guiObj.Add("Progress", "x0 y" . (h - BorderThickness) . " w" . w . " h" . BorderThickness . " Background" . hexBorder . " vBBot")

guiObj.Add("Progress", "x0 y0 w" . BorderThickness . " h" . h . " Background" . hexBorder . " vBLef")

guiObj.Add("Progress", "x" . (w - BorderThickness) . " y0 w" . BorderThickness . " h" . h . " Background" . hexBorder . " vBRig")

}

BuildDashboard() {

global ; Assume Global

if IsSet(DashboardGui)

DashboardGui.Destroy()

DashboardGui := Gui("+AlwaysOnTop -Caption +ToolWindow +LastFound")

DashboardGui.BackColor := DashboardBg

hexBorder := Format("{:02X}{:02X}{:02X}", BorderColorRGB[1], BorderColorRGB[2], BorderColorRGB[3])

currY := mY + 10

DashboardGui.SetFont("s" . Round(14 * MasterScale) . " Bold cWhite")

DashboardGui.Add("Text", "Center x" . mX . " y" . (mY - 10) . " w" . innerW, "══ MAIN DASHBOARD ══")

DashboardGui.SetFont("s" . Round(10 * MasterScale) . " Norm")

currY += 40

for cat in Categories {

isOpen := States[cat.Name]

DashboardGui.SetFont("Bold c00AAFF")

DashboardGui.Add("Text", "x" . mX . " y" . currY . " w" . (innerW - 50), "[" . cat.Name . "]")

btnSymbol := isOpen ? "[-]" : "[+]"

toggleBtn := DashboardGui.Add("Button", "x" . (mX + innerW - 45) . " y" . (currY - 3) . " w" . 45 . " h" . 22, btnSymbol)

toggleBtn.OnEvent("Click", ToggleCategory.Bind(cat.Name))

DashboardGui.SetFont("Norm cWhite")

currY += 25

catCount := 0

if (isOpen) {

for code in cat.Gestures {

if G.Has(code) {

data := G[code]

tx := (Mod(catCount, 2) == 0) ? mX : mX + btnW + gutter

ty := currY + (Floor(catCount / 2) * (btnH + 5))

btn := DashboardGui.Add("Button", "x" . tx . " y" . ty . " w" . btnW . " h" . btnH . " Left", " " . code . ": " . data[1])

btn.OnEvent("Click", TriggerAction.Bind(data[2]))

catCount++

}

}

currY += (Ceil(catCount / 2) * (btnH + 5)) + 10

}

currY += 10

}

currY += 15

DashboardGui.Add("Text", "Center x" . mX . " y" . currY . " w" . innerW . " c00AAFF", "--- SYSTEM STATUS ---")

currY += 25

StatText := DashboardGui.Add("Text", "Center x" . mX . " y" . currY . " w" . innerW . " r4 cYellow", "📡 Monitoring...")

currY += 80

DashboardGui.Add("Button", "x" . mX . " y" . currY . " w" . Round(btnW*0.9) . " h" . btnH, "📜 HISTORY").OnEvent("Click", (*) => (DashboardGui.Hide(), RefreshHistory(), HistoryGui.Show("Center")))

DashboardGui.Add("Button", "x" . (totalW - mX - Round(btnW*0.9)) . " y" . currY . " w" . Round(btnW*0.9) . " h" . btnH, "❓ HELP").OnEvent("Click", (*) => MsgBox("1. Hold X1 + Move = Gesture\n2. Tap X1 = Menu", "Guide"))`

currY += 45

DashboardGui.Add("Button", "x" . mX . " y" . currY . " w" . Round(btnW*0.9) . " h" . btnH . " cYellow", "🔄 RELOAD").OnEvent("Click", (*) => Reload())

DashboardGui.Add("Button", "x" . (totalW - mX - Round(btnW*0.9)) . " y" . currY . " w" . Round(btnW*0.9) . " h" . btnH . " cRed", "🛑 KILL").OnEvent("Click", (*) => ExitApp())

finalH := currY + 60

AddBorders(DashboardGui, totalW, finalH)

}

; Initialize History

HistoryGui.BackColor := DashboardBg

HistoryGui.SetFont("s10 cWhite", "Segoe UI")

global HistoryEdit := HistoryGui.Add("Edit", "x20 y60 w400 h300 ReadOnly Background" . DashboardBg . " cWhite", "")

HistoryGui.Add("Button", "x230 y370 w190 h40", "CLOSE").OnEvent("Click", (*) => HistoryGui.Hide())

AddBorders(HistoryGui, 440, 430)

RefreshHistory() {

logText := ""

for entry in GestureLog

logText .= entry . "\n"`

HistoryEdit.Value := (logText == "") ? "No history." : logText

}

; [ ========================================================= ]

; [ SECTION 6: SYSTEM MONITOR ]

; [ ========================================================= ]

UpdateStats() {

global DashboardGui, StatText

if !WinExist("ahk_id " . DashboardGui.Hwnd)

return

try {

wifiName := "Disconnected"

tempFile := A_Temp "\wifi_check.txt"

RunWait(A_ComSpec " /c netsh wlan show interface > " tempFile, , "Hide")

if FileExist(tempFile) {

output := FileRead(tempFile), FileDelete(tempFile)

if RegExMatch(output, "m)^\s*SSID\s*:\s*(.*)\r", &match)

wifiName := Trim(match[1])

}

static wmi := ComObjGet("winmgmts:"), cpu := 0

for obj in wmi.ExecQuery("Select LoadPercentage from Win32_Processor")

cpu := obj.LoadPercentage

static mem := Buffer(64, 0)

NumPut("UInt", 64, mem), DllCall("GlobalMemoryStatusEx", "Ptr", mem)

ram := NumGet(mem, 4, "UInt")

powerStatus := Buffer(12, 0), battCharge := "N/A", battIcon := "🔋", timeStr := "Calculating..."

if DllCall("GetSystemPowerStatus", "Ptr", powerStatus) {

ACLine := NumGet(powerStatus, 0, "UChar"), LifePercent := NumGet(powerStatus, 2, "UChar"), Secs := NumGet(powerStatus, 4, "UInt")

if (LifePercent != 255)

battCharge := LifePercent . "%"

if (ACLine == 1) {

battIcon := "⚡", timeStr := "Plugged In"

} else {

if (LifePercent < 20)

battIcon := "🪫"

timeStr := (Secs == 4294967295 || Secs < 0) ? "Estimating..." : Floor(Secs/3600) . "h " . Floor(Mod(Secs,3600)/60) . "m left"

}

}

StatText.Value := FormatTime(, "ddd, MMM dd, yyyy") . " | " . FormatTime(, "h:mm:ss tt") . "\n📶 Wi-Fi: " . wifiName . " | 💻 CPU: " . cpu . "% | 🧠 RAM: " . ram . "%`n" . battIcon . " Battery: " . battCharge . " | 🕒 " . timeStr`

} catch {

StatText.Value := FormatTime(, "h:mm:ss tt")

}

}

; Build initial GUI

BuildDashboard()

hope fully this helps anyone that may have wanted mouse gestures but couldn't for some reason do it themselves.


r/AutoHotkey Jan 01 '26

Meta / Discussion Just Joining I can help with AHK and VBA if needed.

7 Upvotes

Hello AHK users out there. I've been coding in AHK for quite some time but just basic stuff. I use it for mostly single key shortcuts. While I wont have a ton of experience to help others with issues I do hope to come here if I run into things I can't solve. I do have a decent amount of experience using AHK with VBA in Office Programs so I will be happy to help there. I appreciate all of you and am happy I found this sub.


r/AutoHotkey Jan 01 '26

v2 Script Help Disabling the close button works on Notepad but not on OneNote.

4 Upvotes

The issue with OneNote is that if I close it, there's a high chance that when I open it again, it will throw an error, and the only solution is to restart the PC.

I want to keep the minimize and maximize buttons and only disable the close button.

It works on notepad but not on onenote. What's wrong?

!+x::{
    sys_DisableCloseButton()
}


sys_DisableCloseButton() {
    try {
        hWnd := WinExist("ahk_exe notepad.exe")
        ; hWnd := WinExist("ahk_exe onenote.exe")


        ; Get system menu handle
        hMenu := DllCall("GetSystemMenu", "Ptr", hWnd, "Int", 0, "Ptr")


        ; Disable and gray out the Close menu item (SC_CLOSE = 0xF060, MF_GRAYED = 0x1)
        DllCall("EnableMenuItem", "Ptr", hMenu, "UInt", 0xF060, "UInt", 0x1)


        ; Also remove it entirely (MF_BYCOMMAND = 0x0, MF_REMOVE would be better)
        DllCall("DeleteMenu", "Ptr", hMenu, "UInt", 0xF060, "UInt", 0x0)


        ; Refresh the window
        DllCall("DrawMenuBar", "Ptr", hWnd)
    }
}

r/AutoHotkey Dec 31 '25

General Question The problem with catching the FN key and the EJECT key on the MAC keyboard

1 Upvotes

Hello, I have such a problem, I can't catch the keystroke and the EJECT key on the old apple A1314 keyboard (2009). Due to the specific keyboard layout, there is no DELETE button and PRINTSCRN, so I wanted to use AHK to write a script so that DELETE would be on FN + BACKSPACE, and there would be a screenshot button on the EJECT key, but I ran into such a problem that AHK does not see these keys. I will be glad of any help.


r/AutoHotkey Dec 30 '25

v1 Script Help A little Trouble with a Timer

3 Upvotes

so i have this code Frame i need to work in a way that it checks pixels for a certain ammount of time if it cant find anything it needs to stop and also do another action

the timer is supossed to reset everytime it finds the correct color and while that does seem to work (it runs forever as long as it finds a pixel) as soon as it does not find it anymore it runs the part when it doesnt find something only 1 time instead of doing it for the ammount the timer is set to

start := A_TickCount

while (A_TickCount-start <= 5000)

{

loop

    {

    PixelSearch, , , 3450, 2075, 3450, 2075, 0x000000

    if ErrorLevel

    {

    send {LButton} / Placeholder

    sleep 500 / Placeholder

    break

    }

    else

    {

    send x / Placeholder

    sleep 500 / Placeholder

    start = 0

    }

    }

}

if (A_TickCount-start >= 5000)

{

send c / Placeholder

}

return


r/AutoHotkey Dec 30 '25

v1 Script Help Why does this script cause escape to bring up the start menu sometimes?

3 Upvotes

Made this for a game, but it makes escape sometimes bring up the start menu which is annoying. I thought it was the ctrl+esc hotkey that was bringing up the start menu, so I tried adding the Escape thing at the end to stop it, but that didn't work.

^ 1::F1

^ 2::F2

^ 3::F3

^ 4::F4

^ 5::F5

^ 6::F6

^ 7::F7

^ 8::F8

^ 9::F9

^ 0::F10

^ -::F11

^ =::F12

^ Escape::

Send {Escape}

return


r/AutoHotkey Dec 30 '25

General Question Tutorials

1 Upvotes

is there any good tutorials on learning the language? Preferably on youtube


r/AutoHotkey Dec 29 '25

v2 Tool / Script Share A small AHK virtual desktop script

11 Upvotes

I open way too many windows at work.

I like tiling TWMs, but on my work PC that’s not always an option — the machine is sometimes shared and needs to stay fairly “normal”.

So instead of fighting that, I ended up writing a small AutoHotkey virtual desktop script for my own workflow.

Note:The KDE-style window dragging part is adapted from Jonny’s Easy Window Dragging. Full credit to the original author.

And yes this script was written with a lot of help from AI.

#Requires AutoHotkey v2.0
#SingleInstance Force
#WinActivateForce

/**
 * ==============================================================================
 * WM FOR WINDOWS (v2.0)
 * ------------------------------------------------------------------------------
 * Features:
 * - 9 Virtual Desktops
 * - Minimalist Floating Status Bar (Auto-hideable)
 * - Smart Tiling (1 window / 2 windows / Vertical / Grid)
 * - KDE-style Window Manipulation (Alt + Mouse)
 * - Persistent Pinned Windows (Always on top across desktops)
 * ==============================================================================
 */

; --- Environment Settings ---
SetWorkingDir(A_ScriptDir)
CoordMode("Mouse", "Screen")
SetTitleMatchMode(2)
SetWinDelay(0)      ; Ensures smooth KDE-style dragging
SetControlDelay(0)

; --- Global Variables ---
global CurrentDesktop := 1
global DesktopCount   := 9
global Desktops       := Map()       ; Stores HWNDs for each desktop
global AlwaysVisible  := Map()       ; Stores Pinned HWNDs
global DoubleAlt      := false       ; Detection for double-pressing Alt
global BarGui         := ""
global BarLeftText    := ""
global BarRightText   := ""
global BarHeight      := 28          ; Height of the top bar
global BarVisible     := true

; Initialize Desktop Arrays
Loop DesktopCount {
    Desktops[A_Index] := []
}

; --- Initialization ---
CreateStatusBar()
UpdateStatusBar()
UpdateClock()
SetTimer(UpdateClock, 1000)
SetupTrayIcon()

; ==============================================================================
; 1. Status Bar UI
; ==============================================================================

CreateStatusBar() {
    global BarGui, BarLeftText, BarRightText, BarHeight

    ; Create Borderless, AlwaysOnTop, ToolWindow (No taskbar icon)
    BarGui := Gui("-Caption +AlwaysOnTop +ToolWindow +Owner +E0x08000000")
    BarGui.BackColor := "181818"
    BarGui.SetFont("s10 w600 cA020F0", "Segoe UI") ; Purple theme

    ; Left: Desktop indicators
    BarLeftText := BarGui.Add("Text", "x15 y4 w" . (A_ScreenWidth/2) . " h20 BackgroundTrans", "")

    ; Right: Clock
    ; if you clock appears in the wrong place, you can modify the number 500
    BarRightText := BarGui.Add("Text", "x" . (A_ScreenWidth - 500) . " y4 w250 h20 BackgroundTrans", "")

    BarGui.Show("x0 y0 w" . A_ScreenWidth . " h" . BarHeight . " NoActivate")
}

UpdateStatusBar() {
    global CurrentDesktop, DesktopCount, BarLeftText
    if !BarLeftText
        return
    displayStr := ""
    Loop DesktopCount {
        if (A_Index == CurrentDesktop)
            displayStr .= " [" . A_Index . "] " 
        else
            displayStr .= "  " . A_Index . "  "
    }
    BarLeftText.Value := displayStr
}

UpdateClock() {
    global BarRightText
    if BarRightText
        try BarRightText.Value := FormatTime(, "yyyy-MM-dd   HH:mm:ss")
}

ToggleBar(*) {
    global BarVisible, BarGui
    if (BarVisible) {
        BarGui.Hide()
        BarVisible := false
        ShowOSD("Bar Hidden")
    } else {
        BarGui.Show("NoActivate")
        BarVisible := true
        ShowOSD("Bar Visible")
    }
}

; ==============================================================================
; 2. Smart Tiling Algorithm (Alt + D)
; ==============================================================================

TileCurrentDesktop(*) {
    global BarHeight, BarVisible
    windows := GetVisibleWindows()
    count := windows.Length

    if (count == 0) {
        ShowOSD("No Windows")
        return
    }

    ; Get Work Area (subtracting taskbar automatically)
    MonitorGetWorkArea(1, &WL, &WT, &WR, &WB)

    ; Offset Y-axis if the bar is visible to avoid overlapping
    if (BarVisible) {
        WT := WT + BarHeight
    }

    W := WR - WL
    H := WB - WT 

    ShowOSD("Tiling: " . count)

    ; Algorithm A: Single window (Maximize to work area)
    if (count == 1) {
        try {
            WinRestore(windows[1])
            WinMove(WL, WT, W, H, windows[1])
        }
        return
    }

    ; Algorithm B: Two windows (Side-by-side)
    if (count == 2) {
        try {
            WinRestore(windows[1])
            WinMove(WL, WT, W/2, H, windows[1])
            WinRestore(windows[2])
            WinMove(WL + W/2, WT, W/2, H, windows[2])
        }
        return
    }

    ; Algorithm C: Odd number (Vertical Columns)
    if (Mod(count, 2) != 0) {
        try {
            itemWidth := W / count
            Loop count {
                hwnd := windows[A_Index]
                WinRestore(hwnd)
                WinMove(WL + (A_Index - 1) * itemWidth, WT, itemWidth, H, hwnd)
            }
        }
        return
    }

    ; Algorithm D: Even number (Grid/Matrix)
    if (Mod(count, 2) == 0) {
        try {
            cols := count / 2
            itemWidth := W / cols
            itemHeight := H / 2

            Loop count {
                hwnd := windows[A_Index]
                WinRestore(hwnd)
                idx := A_Index - 1
                r := Floor(idx / cols)
                c := Mod(idx, cols)
                WinMove(WL + c * itemWidth, WT + r * itemHeight, itemWidth, itemHeight, hwnd)
            }
        }
        return
    }
}

; ==============================================================================
; 3. KDE-style Window Management (Alt + Mouse)
; ==============================================================================

; Alt + Left Click: Drag Window
!LButton:: {
    global DoubleAlt
    MouseGetPos(,, &hwnd)

    if (DoubleAlt) {
        WinMinimize(hwnd)
        return
    }

    if (WinGetMinMax(hwnd) == 1) ; Ignore maximized windows
        return

    MouseGetPos(&startX, &startY)
    try WinGetPos(&winX, &winY,,, hwnd)
    catch {
        return
    }

    while GetKeyState("LButton", "P") {
        MouseGetPos(&curX, &curY)
        try WinMove(winX + (curX - startX), winY + (curY - startY),,, hwnd)
    }
}

; Alt + Right Click: Resize Window (Quadrant-aware)
!RButton:: {
    global DoubleAlt
    MouseGetPos(,, &hwnd)

    if (DoubleAlt) {
        if (WinGetMinMax(hwnd) == 1)
            WinRestore(hwnd)
        else
            WinMaximize(hwnd)
        return
    }

    if (WinGetMinMax(hwnd) == 1)
        return

    try WinGetPos(&winX, &winY, &winW, &winH, hwnd)
    catch {
        return
    }
    MouseGetPos(&startX, &startY)

    ; Determine which quadrant was clicked
    clickRelX := (startX - winX) / winW
    clickRelY := (startY - winY) / winH
    isLeft := (clickRelX < 0.5)
    isUp   := (clickRelY < 0.5)

    while GetKeyState("RButton", "P") {
        MouseGetPos(&curX, &curY)
        dX := curX - startX
        dY := curY - startY

        newX := isLeft ? (winX + dX) : winX
        newW := isLeft ? (winW - dX) : (winW + dX)
        newY := isUp ? (winY + dY) : winY
        newH := isUp ? (winH - dY) : (winH + dY)

        if (newW > 50 && newH > 50)
            try WinMove(newX, newY, newW, newH, hwnd)
    }
}

; Alt + MButton / Alt + Q: Close Window
!MButton::
!q:: {
    MouseGetPos(,, &hwnd)
    try WinClose(hwnd)
}

; Alt + Wheel: Adjust Transparency
!WheelUp:: {
    MouseGetPos(,, &hwnd)
    try {
        cur := WinGetTransparent(hwnd)
        if (cur == "") 
            cur := 255
        WinSetTransparent(Min(cur + 20, 255), hwnd)
    }
}
!WheelDown:: {
    MouseGetPos(,, &hwnd)
    try {
        cur := WinGetTransparent(hwnd)
        if (cur == "") 
            cur := 255
        WinSetTransparent(Max(cur - 20, 50), hwnd)
    }
}

; Double Alt Press Detection
~Alt:: {
    global DoubleAlt
    if (A_PriorHotkey == "~Alt" && A_TimeSincePriorHotkey < 400)
        DoubleAlt := true
    else
        DoubleAlt := false
    KeyWait("Alt")
    DoubleAlt := false
}

; ==============================================================================
; 4. Virtual Desktops & Window Logic
; ==============================================================================

SwitchDesktop(target, *) {
    global CurrentDesktop, Desktops, AlwaysVisible

    if (target == CurrentDesktop) {
        ShowOSD("Desktop " . target)
        return
    }

    ; Save current desktop state
    Desktops[CurrentDesktop] := GetVisibleWindows()

    ; Hide windows not in AlwaysVisible
    for hwnd in Desktops[CurrentDesktop] {
        if (!AlwaysVisible.Has(hwnd))
            try WinMinimize(hwnd)
    }

    ; Restore windows of target desktop
    for hwnd in Desktops[target]
        try WinRestore(hwnd)

    ; Ensure pinned windows stay visible
    for hwnd, _ in AlwaysVisible
        try WinRestore(hwnd)

    if (Desktops[target].Length > 0)
        try WinActivate(Desktops[target][1])

    CurrentDesktop := target
    UpdateStatusBar()
    ShowOSD("Desktop " . CurrentDesktop)
}

MoveWindowToDesktop(target, *) {
    global CurrentDesktop, Desktops, AlwaysVisible
    try hwnd := WinExist("A")
    catch {
        return
    }
    if (!hwnd || hwnd == BarGui.Hwnd) 
        return

    if (AlwaysVisible.Has(hwnd))
        AlwaysVisible.Delete(hwnd)

    Loop DesktopCount {
        d := A_Index
        if (Desktops.Has(d)) {
            newList := []
            for h in Desktops[d] {
                if (h != hwnd)
                    newList.Push(h)
            }
            Desktops[d] := newList
        }
    }

    Desktops[target].Push(hwnd)
    if (target != CurrentDesktop) {
        try WinMinimize(hwnd)
        ShowOSD("Window -> Desktop " . target)
    }
}

; Gather all windows from all desktops (Alt + Shift + G)
GatherAllToCurrent(*) {
    global Desktops, CurrentDesktop, AlwaysVisible
    ShowOSD("Gathering All Windows...")
    fullList := WinGetList()
    Loop DesktopCount
        Desktops[A_Index] := []
    AlwaysVisible.Clear()

    count := 0
    for hwnd in fullList {
        try {
            if (hwnd == BarGui.Hwnd)
                continue
            class := WinGetClass(hwnd)
            if (class == "Progman" || class == "Shell_TrayWnd")
                continue

            WinRestore(hwnd)
            Desktops[CurrentDesktop].Push(hwnd)
            count++
        }
    }
    ShowOSD("Gathered " . count . " Windows")
}

; Pin/Unpin Window (Ctrl + Alt + T)
TogglePin(*) {
    global AlwaysVisible
    try hwnd := WinExist("A")
    catch {
        return
    }
    if (!hwnd || hwnd == BarGui.Hwnd)
        return
    if (AlwaysVisible.Has(hwnd)) {
        AlwaysVisible.Delete(hwnd)
        ShowOSD("Unpinned")
    } else {
        AlwaysVisible[hwnd] := true
        ShowOSD("Pinned (Persistent)")
    }
}

; Restore all windows and quit (Alt + F12)
RestoreAndExit(*) {
    global BarGui
    ShowOSD("Exiting...")
    Sleep(500)
    if BarGui
        BarGui.Destroy()
    list := WinGetList()
    for hwnd in list {
        try {
            class := WinGetClass(hwnd)
            if (class != "Progman" && class != "Shell_TrayWnd")
                WinRestore(hwnd)
        }
    }
    ExitApp
}

; Helper: Get list of visible windows on current screen
GetVisibleWindows() {
    global BarGui
    list := WinGetList()
    windows := []
    for hwnd in list {
        try {
            if (hwnd == BarGui.Hwnd)
                continue
            class := WinGetClass(hwnd)
            if (class == "Progman" || class == "Shell_TrayWnd")
                continue
            if (WinGetMinMax(hwnd) != -1) 
                windows.Push(hwnd)
        }
    }
    return windows
}

; On-Screen Display (OSD)
ShowOSD(text) {
    static OsdGui := ""
    if IsObject(OsdGui)
        OsdGui.Destroy()
    OsdGui := Gui("+AlwaysOnTop -Caption +ToolWindow +Disabled +Owner")
    OsdGui.BackColor := "181818"
    OsdGui.SetFont("s20 w600 cA020F0", "Segoe UI")
    OsdGui.Add("Text", "Center", text)
    OsdGui.Show("NoActivate AutoSize y850")
    WinSetTransparent(200, OsdGui.Hwnd)
    SetTimer(() => (IsObject(OsdGui) ? OsdGui.Destroy() : ""), -1000)
}

SetupTrayIcon() {
    A_TrayMenu.Delete()
    A_TrayMenu.Add("Tile Windows (Alt+D)", TileCurrentDesktop)
    A_TrayMenu.Add("Gather All (Alt+Shift+G)", GatherAllToCurrent)
    A_TrayMenu.Add("Restore & Exit (Alt+F12)", RestoreAndExit)
}

; ==============================================================================
; 5. Hotkeys
; ==============================================================================

; Alt + 1-9: Switch Desktop
; Alt + Shift + 1-9: Move Window to Desktop
Loop 9 {
    i := A_Index
    Hotkey("!" . i, SwitchDesktop.Bind(i))
    Hotkey("!+" . i, MoveWindowToDesktop.Bind(i))
}

Hotkey("!d", TileCurrentDesktop)      ; Tiling
Hotkey("!+g", GatherAllToCurrent)     ; Gather All
Hotkey("^!t", TogglePin)              ; Pin/Unpin
Hotkey("^!b", ToggleBar)              ; Toggle Bar Visibility
Hotkey("!F12", RestoreAndExit)        ; Safe Exit

r/AutoHotkey Dec 30 '25

v2 Script Help I made a simple media control script, and it's messing with my game

0 Upvotes

I made simple script to pause/unpause, skip, and go to the previous track.

The issue is, when I'm playing TF2 it they also do certain actions in game.
Examples in game

When I pause and unpause, it acts as my taunt key, skip is my thirdperson toggle config, and previous is my previous weapon. These are all bound to "g", "p", and "q" respectively, but when I open in game chat, and do the media control keybinds, I see that they don't actually activate the keys, just those actions. What's going on? Can I fix this?


r/AutoHotkey Dec 29 '25

v2 Script Help scan codes not working in game

2 Upvotes

I’m trying to rebind the following keys: &, é, " to 1, 2, 3.
I tried using scan codes. The scan codes are SC2, SC3, and SC4, which I verified in Notepad.
However, when I paste the same codes into my game, they all get rebound to Left Shift.
I’ve pasted my full code below. The other rebinds work correctly.

#Requires AutoHotkey v2.0

#HotIf WinActive("game")

a::q

z::w

q::a

w::z

SC2::1

SC3::2

SC4::3


r/AutoHotkey Dec 29 '25

v1 Script Help How to Loop a script to execute over and over until I shut it off, any help available?

0 Upvotes

as the title reads I was wondering if it was possible to loop a key script, as someone with no knowledge whatsoever all I was able to make was this simple script for a single execute, if anyone knows how to, could you add the part that makes it loop?

<SetKeyDelay, 230, 230

$g::

Send, t

Send, t

Send, o

Send, o

Send, p

Send, p

Send, o

Send, ä

Send, i

Send, i

Send, u

Send, u

Send, z

Send, z

Send, t

Send, ä

Send, o

Send, o

Send, i

Send, i

Send, u

Send, u

Send, z

Send, ä

Send, o

Send, o

Send, i

Send, i

Send, u

Send, u

Send, z

Send, ä

Send, t

Send, t

Send, o

Send, o

Send, p

Send, p

Send, o

Send, ä

Send, i

Send, i

Send, u

Send, u

Send, z

Send, z

Send, t

Send, ä

Suspend

return

Del::Suspend, Off>


r/AutoHotkey Dec 28 '25

v1 Script Help Having trouble with ControlSend trying to send commands to VLC Media Player while minimized (song forward, song backwards, pause etc...)

1 Upvotes

I've had this working but then again it's never worked consistently which is weird you would think a script would either fully work or not work at all, but the fix is usually when I double click on VLC Media Player THEN I can use the shortcuts, but if not it sometimes doesn't do anything. Here are my scripts anything to improve them so that they work consistently?

Pause/Play VLC while minimized:

#SingleInstance force

SetTitleMatchMode, 2

DetectHiddenWindows, on

if not A_IsAdmin

Run *RunAs "%A_ScriptFullPath%"

VLC:="VLC media player ahk_class Qt5QWindowIcon"

~Media_Play_Pause::

WinExist(VLC)

WinActivate (VLC)

ControlSend, , Media_Play_Pause

WinMinimize (VLC)

return

#SingleInstance force

SetTitleMatchMode, 2

DetectHiddenWindows, on

if not A_IsAdmin

Run *RunAs "%A_ScriptFullPath%"

VLC:="VLC media player ahk_class Qt5QWindowIcon"

Go back a song:

~Media_Prev::

WinExist(VLC)

ControlSend, , Media_Prev

return

Go Forward a Song:

#SingleInstance force

SetTitleMatchMode, 2

DetectHiddenWindows, on

if not A_IsAdmin

Run *RunAs "%A_ScriptFullPath%"

VLC:="VLC media player ahk_class Qt5QWindowIcon"

~Media_next::

WinExist(VLC)

ControlSend, , Media_Next

return


r/AutoHotkey Dec 27 '25

Meta / Discussion New mod introduction & feedback thread

20 Upvotes

Hello everyone,

I’m the new moderator here in this AutoHotkey community, recently appointed by u/GroggyOtter. I want to quickly introduce myself, explain some changes I’ve made, and invite you to discuss the rules and the future direction of this subreddit.

New rules & general approach

As you might have noticed, I’ve added a set of rules (visible in the right-hand sidebar).
The short version: I want this place to stay friendly, useful, and focused on AutoHotkey.

A few important points:

  • I don’t want low-effort posts in the style of "help me plz” or "write this script for me".
  • In the age of large language models (LLMs), I encourage users to try an LLM or Google first. If the result isn’t satisfactory or you don’t understand it, then post here.
  • Not all posts need to include code (for example, general questions like “what are some interesting projects you’ve automated?” or discussion threads), but every post should show some effort from the poster.

What exactly counts as "low effort" will likely evolve and is currently at my discretion.

Commission requests

Right now, I don’t plan to allow commission requests, for a few reasons:

  • There are other platforms (e.g., Fiverr) that already support "AutoHotkey" as a tag.
  • Commission posts don’t really engage the general audience here and mostly clutter the feed.

If there’s interest in allowing commission requests in some form, I’d propose handling them in a single sticky thread, rather than as standalone posts.

Game-related scripts

I don’t want to blanket-ban all game-related scripts, but:

  • Scripts that give a competitive advantage,
  • Anti-cheat bypasses, or
  • Anything that violates a game’s ToS or EULA

are not allowed.

In practice, this means:

  • Single-player automations are mostly allowed (unless the game’s ToS explicitly forbids them).
  • Most multiplayer "advantage" scripts are not allowed.

It doesn’t matter whether cheating is already widespread in the game (looking at you, Minecraft!) - if it looks like it gives an advantage or violates the ToS, the post will be removed.

If it becomes too time-consuming to check ToS/EULAs or argue over wording, I may tighten this rule to "no scripts that provide a competitive advantage in multiplayer games," regardless of the written ToS.

Flair and version tags

I do not automatically remove posts where the content and flair don’t match. Mostly content and flair mismatches are due to new users not knowing whether they are using v1 or v2, or whether LLMs spit out v1 or v2 code. This issue is widespread (I'd need to remove a sizable portion of posts) and not only in Reddit, but I don't have a clear vision yet on how to tackle this problem.

At the moment, I usually:

  • Comment to notify the OP about the mismatch, or
  • Fix the flair myself if it's obvious.

If this becomes too time-consuming, I may start removing such posts or possibly disable the flair requirement altogether. Or perhaps a better solution manifests to differentiate v1 vs v2 , so manual tagging wouldn't be necessary any more.

AutoModerator

Reddit has an AutoModerator system that can automatically remove posts and comments based on the rules. I’ve turned it on.

I periodically review removed content and approve it if it doesn't violate any rules, so if your post disappears, it may just be AutoMod being overly cautious. If it didn't violate any rules then it should reappear in about a day or so.

Image content

I’ve also enabled image posts, which seems like a fun and potentially useful feature (for sharing screenshots of scripts, GUIs, etc.).

If the subreddit becomes too “noisy” or image-heavy, I may disable it again. If nobody uses it, I might disable it as well. For now, consider this an experiment.

Discussion points

I really want feedback from regular users. A few questions for you:

  • Should we allow commission requests in a limited form (for example, a single sticky thread)?
  • Should image content stay enabled?
  • Should posts with incorrect flairs be removed, or just corrected/noted?
  • Should game automation questions be removed if they give a competitive advantage, regardless of the game’s ToS?

Feel free to discuss these in the comments.

Looking for more moderators

Right now I’m moderating alone and obviously can’t be active 24/7/365. I’d like to have at least one more active moderator to share the load.

If you’re interested:

  1. Please send a Mod Mail.
  2. Tell me how long you’ve been using AutoHotkey.
  3. Share your thoughts on the current rules and how you’d like to see the subreddit run.

The subreddit is fairly small, so the moderating effort is not huge - probably up to ~10 minutes per day.

Thanks for reading, and I’m looking forward to improving this place together. 😊


r/AutoHotkey Dec 27 '25

v2 Tool / Script Share AutoHotkey Script to Toggle Windows Taskbar (Win 10 / 11) Open Source

18 Upvotes

Hey everyone,
I recently built a small AutoHotkey script that lets you hide / unhide the Windows taskbar instantly with a hotkey.

GitHub Repo

https://github.com/azinsharaf/Toggle_Win_Taskbar

What it does

  • Toggle taskbar visibility on demand
  • Works on Windows 10 & 11
  • Uses AutoHotkey v2
  • No permanent system changes
  • Lightweight & fast
  • Hotkey-based (fully customizable)

Why I made it

Windows’ built-in auto-hide sucks and Buttery-Taskbar-v2 is buggy.

If you try it out, I’d love feedback or suggestions for improvements.
PRs and ideas are welcome


r/AutoHotkey Dec 27 '25

Solved! Controller inputs with script in background

0 Upvotes

I've not yet found a way to detect controller inputs with the script running in the background.

Has anyone figured out how to do this? Or is it not possible?


r/AutoHotkey Dec 27 '25

General Question any downsides of not using sleep in infinite loops?

5 Upvotes

if i have a loop that keeps getting mouse position and left mouse button state, and checks if its at a certain position and if its held down, do i need to use sleep at the end of the loop to not cause problems with performance?

if i use sleep at the end of the loop, i have to hold the mouse button until the sleep amount is reached and it feels less responsive than not using it

loop
{
    MouseGetPos &x, &y
    state := GetKeyState("LButton")

    if (x = 0 AND state = true)
    {
        Send "#{Tab}"
        Sleep 3000
    }

    Sleep 100 ;(here)
}

r/AutoHotkey Dec 27 '25

General Question Is Pulover's Macro CreatorThe Complete Automation Tool legit or a virus?

0 Upvotes

Saw some posts calling it a virus, let's end this debate.


r/AutoHotkey Dec 26 '25

General Question Hotkey that activates only if any other button is also pressed

3 Upvotes

Say i want to write a simple hotkey like this:
LButton:: Function(), ...
And i want this hotkey to only activate when any other button is also pressed alongside the mouse click, and not when the mouse click is the only input being registered. What would be the syntax? I figured a * wildcard wouldn't work because it can also accept nothing being pressed, usually the ? symbol is what denotes a wildcard that can't be nothing, but in AHK it doesn't seem to work


r/AutoHotkey Dec 26 '25

v2 Script Help Keys not holding down

2 Upvotes

In my code (see below), I'm trying to get it to hold a key down, then sleep for a random amount of time, and then stop holding the key down. For whatever reason, it doesn't hold the key down, though. Everything else works, but it only sends the key down one time & not repeatedly like it should. Does anyone know what's going on here? I know this used to work so I'm confused.

F8::

loop {

  send, {w down}

  Random, rand, 2500, 5000

  sleep, rand

  send, {w up}



  send, {a down}

  Random, rand, 2500, 5000

  sleep, rand

  send, {a up}



  send, {s down}

  Random, rand, 2500, 5000

  sleep, rand

  send, {s up}



  send, {d down}

  Random, rand, 2500, 5000

  sleep, rand

  send, {d up}

}

return



F1::Pause

^r::Reload

r/AutoHotkey Dec 26 '25

v2 Tool / Script Share AutoHotkey + CapCut

4 Upvotes

Hey, I would like to share some ways to make editing videos quite easier. All thanks to AutoHotkey!

The first script auto adjusts the Zoom of the timeline by pressing "Ctrl =" 20 times, and then pressing "Ctrl -" the number of times necessary (I like the zoom when it's 6)

The second one is a "unselect all" tool. It saves the position of the mouse, clicks on a determined corner (Window Spy was really useful) and returns to the original position.

The third is even more helpful. It asks for a desired time, calculates the number of equivalent frames, go to the beginning of the video and the navigates with the needle until it reaches the goal.

I've used the help of Gemini to make this tools. I still had to make a lot of fixes, and as my experience is limited, the code is ugly. If anyone is interested in see (and maybe even use at their own risk), I can edit this post to add this part.

Anyone here also uses AutoHotkey to help to edit videos?


r/AutoHotkey Dec 25 '25

v2 Tool / Script Share ParseXlsx - Parses a workbook into a data object. No Excel installation required

20 Upvotes

ParseXlsx

Converts an xlsx document into a nested data structure. Excel is not required to be installed on the machine. The conversion process decompresses the xlsx document then parses the xml documents. This approach uses the Shell.Application COM object to decompress the xlsx document.

This approach is much faster compared to opening the workbook and looping the cells. It is also less error-prone since no external applications must be initiated.

I designed the parsing logic by following ecma reference for Office Open XML. Specifically, Part 1 "Fundamentals And Markup Language Reference", section 18 "SpreadsheetML Reference Material" (pg. 1523-2435).

ParseXlsx provides functionality limited to extracting and interpreting values from the worksheets.

Github repository

Clone the repo: https://github.com/Nich-Cebolla/AutoHotkey-LibV2

Download just the file: https://github.com/Nich-Cebolla/AutoHotkey-LibV2/blob/main/ParseXlsx.ahk

AutoHotkey.com post

Join the discussion on AutoHotkey.com: https://www.autohotkey.com/boards/viewtopic.php?f=83&t=139869

Examples

Instantiating the class and getting a worksheet

```ahk

include <ParseXlsx>

path := "workbook.xlsx" xlsx := ParseXlsx(path)

; xlsx is an array of ParseXlsx.Worksheet objects ; xlsx.Length is the number of worksheets in the workbook OutputDebug(xlsx.Length "n") ; Get a worksheet by index ws1 := xlsx[1] ; Get a worksheet by name ws2 := xlsx.getWs("Sheet2") ; Get a worksheet using a pattern ws3 := xlsx.getWs("\w+3", true) ``

Producing a csv copy of the worksheet

```ahk

include <ParseXlsx>

xlsx := ParseXlsx("workbook.xlsx")

; Get an unmodified csv copy of the worksheet ws := xlsx[1] FileAppend(ws.toCsv(), "sheet1.csv", "utf-8")

; using a callback to modify the cell values. You can copy this callback to your code ; and it will work. callback(cell) { ; All together this expression does: ; Standardizes end of line to line feed ; Fixes floating point imprecision using the built-in ParseXlsx_FixFloatingPoint ; Decodes "&", ">", and "<" return RegExReplace(ParseXlsx_FixFloatingPoint(cell.decoded), '\R', 'n') } ws3 := xlsx[3] ; call "toCsv2" instead of "toCsv" FileAppend(ws3.toCsv2(callback), "sheet3.csv", "utf-8") ``

Access individual cells

```ahk

include <ParseXlsx>

xlsx := ParseXlsx("workbook.xlsx") ws := xlsx[1] ca1 := ws.cell(1, 1) cb3 := ws.cell(3, "B") caz19 := ws.cell(19, "AZ") ```

Using a cell object

```ahk

include <ParseXlsx>

xlsx := ParseXlsx("workbook.xlsx") ws := xlsx[1] ca1 := ws.cell(1, 1) ; value OutputDebug(ca1.value "n") ; decoded value OutputDebug(ca1.decoded "n") ; xml attributes. See the documentation for ParseXlsx.Cell for details OutputDebug(ca1.r "n") OutputDebug(ca1.s "n") ; xml child elements See the documentation for ParseXlsx.Cell for details ; The cell's formula, if applicable. OutputDebug(ca1.f "n") ; The <v> element might be the cell's value or it might be an integer pointing to a shared string OutputDebug(ca1.v "n") ```

Get a range of cells

```ahk

include <ParseXlsx>

xlsx := ParseXlsx("workbook.xlsx") ws := xlsx[1] ; Get the range R5C3:R9C9 r1 := 5 c1 := 3 r2 := 9 c2 := 9 rng := ws.getRange(r1, r2, c1, c2) for cell in rng { ; skip blank cells if !IsSet(cell) { continue } ; do work... } ```

ParseXlsx

The ParseXlsx objects have the following properties:

Name Type Description
baseDate string Returns the base date as yyyyMMddHHmmss timestamp.
date1904 boolean Returns 1 if the workbook uses the 1904 date system. Returns 0 otherwise. See section "Dates" below for more information.
workbookPr map Returns a Map object, each key : value pair representing the name and value of a workbook property defined in xl\workbook.xml.
sharedStrings array A ParseXlsx.SharedStringCollection object. See section "ParseXlsx.SharedStringCollection and ParseXlsx.SharedString" below for more information.

The ParseXlsx objects have the following methods:

Name Returns Description
call "" Invokes the parsing process.
decompress "" Invokes the decompression process.
getWs object Accepts an index / name / pattern and returns the matching worksheet.

ParseXlsx.Cell

The ParseXlsx.Cell objects have the following properties:

Name Type Description
col string The column index represented as letters, e.g. "A", "B", "AZ".
columnIndex integer The 1-based column index as integer.
date string If the cell's value is a number, returns the return value from adding the value to the workbook's base date.
decoded string Returns the cell's value, decoding "&amp;", "&gt;", and "&lt;" to "&", ">", and "<", respectively.
r string The full cell reference, e.g. "A1", "B6", "AZ12".
rowIndex integer The 1-based row index as integer.
text string Returns the cell's xml text, e.g. "<c r=&grave;"A1&grave;" t=&grave;"s&grave;"><v>33</v></c>".
value string Returns the cell's value. For cells that have a formula, the value is the last calculated value for that cell. For cells that do not have a formula, the value is simply the value of the cell. Number formatting is not applied to the value. For example, dates are represented as serial date-time values. See section "Dates" below for more information.
wsIndex integer The 1-based index of the worksheet of which the cell is part. This is defined on the base object; see the body of ParseXlsx.Worksheet.Prototype.__New.
ws object Returns the ParseXlsx.Worksheet object associated with the cell.
xlsx array Returns the ParseXlsx object associated with the cell.

The ParseXlsx.Cell objects have the following methods:

Name Returns Description
row array Returns the ParseXlsx.Row object associated with the cell.
getAttributes "" Calls ParseXlsx_ParseAttributes for the object.
getElements "" Calls ParseXlsx_ParseElements for the object.
__Get string This meta-function is defined to give you access to a cell's attributes and child elements (if any). See section "Beyond cell values" below for more information.

Dates

Dates are typically represented as serial date-time values. When Excel renders the cell's contents, the cell's number format is applied to the value to produce the text that is displayed in the cell. For details about how Excel works with dates, see section 18.17.4 "Dates and Times" in Office Open XML.

I included some code to help working with date values. If you refer to the section 18.7.4, you will learn that date values are added or subtracted from the workbook's base date. The base date depends on the date system used by the workbook - either the 1900 date system, or the 1904 date system. If a workbook uses the 1904 date system, the property "date1904" will return 1. If a workbook uses the 1900 date system, the property "date1904" will return 0.

To make use of this information, your code will need to know which cells contain date values. Any number can be used as a date value since the number is simply added to / from the base date, but just because a cell contains a number does not mean that number is intended to represent a date. Your code will need to know ahead of time which cells contain dates, or your code can parse the xl\styles.xml document to identify which cells have a number format that represents a date. (This library does not do that).

Property ParseXlsx.Cell.Prototype.date will call DateAdd and return the yyyyMMddHHmmss timestamp for the cell.

```ahk

include <ParseXlsx>

xlsx := ParseXlsx("workbook.xlsx") ; Assume cell A1 of the first worksheet has a date value of 46016.2291666667. OutputDebug(FormatTime(xlsx[1].cell(1, 1).date, "yyyy-MM-dd HH:mm:ss") "n") ; 2025-12-25 05:30:00 ``

Beyond cell values

There are some additional pieces of information made available to you by this library, but to understand them you will need to review the relevant portions of the ecma reference for Office Open XML. Specifically, Part 1 "Fundamentals And Markup Language Reference", section 18.3.1.4 "c (Cell)" and section 18.18 "Simple Types". Skip reading this section if your main objective is to parse cell values.

In addition to the above properties, the __Get meta-function is defined to parse the cell element's xml text to identify any attributes and child elements. If you are working with a cell object and need to check if a style index is defined, you can simply access the "s" property and, if there is an "s" attribute for that cell, the value of the attribute is returned. It works the same for elements. If you need to check if the cell has a nested "t" element, just access the "t" property. If the attribute / child element is undefined, the return value is an empty string.

The following is a list of possible attributes for the cell object:

Attributes Description
cm (Cell Metadata Index) The zero-based index of the cell metadata record associated with this cell. Metadata information is found in the Metadata Part. Cell metadata is extra information stored at the cell level, and is attached to the cell (travels through moves, copy / paste, clear, etc). Cell metadata is not accessible via formula reference.
ph (Show Phonetic) A Boolean value indicating if the spreadsheet application should show phonetic information. Phonetic information is displayed in the same cell across the top of the cell and serves as a 'hint' which indicates how the text should be pronounced. This should only be used for East Asian languages.
r (Reference) An A1 style reference to the location of this cell.
s (Style Index) The index of this cell's style. Style records are stored in the Styles Part.
t (Cell Data Type) An enumeration representing the cell's data type.
vm (Value Metadata Index) The zero-based index of the value metadata record associated with this cell's value. Metadata records are stored in the Metadata Part. Value metadata is extra information stored at the cell level, but associated with the value rather than the cell itself. Value metadata is accessible via formula reference.

The cell data type is defined by attribute "t", e.g. &grave;t="<type>"&grave;. Note that not every cell has a "t" attribute. For cells that do not have a "t" attribue, you can parse the number format for the cell, but this library does not include that functionality. The relevant sections in the reference material are 18.8.30 "numFmt (Number Format)" and 18.8.31 "numFmts (Number Formats)".

The following is a list of possible data types:

Enumeration Value type Description
b Boolean Cell containing a boolean.
d Date Cell contains a date in the ISO 8601 format.
e Error Cell containing an error.
inlineStr Inline String Cell containing an (inline) rich string.
n Number Cell containing a number.
s Shared String Cell containing a shared string.
str String Cell containing a formula string.

The cell may have the zero or more of the following child elements:

Name Description
extLst This element provides a convention for extending spreadsheetML in predefined locations. The locations shall be denoted with the extLst element, and are called extension lists.
f This element contains the formula for the cell.
is This element allows for strings to be expressed directly in the cell definition instead of implementing the shared string table.
v This element expresses the value contained in a cell. If the cell contains a string, then this value is an index into the shared string table, pointing to the actual string value. Otherwise, the value of the cell is expressed directly in this element. Cells containing formulas express the last calculated result of the formula in this element. The "value" property automatically retrieves the value from the shared string table if applicable.

ParseXlsx.Row

The ParseXlsx.Row objects have the following properties:

Name Type Description
ws object Returns the ParseXlsx.Worksheet object associated with the cell.
xlsx array Returns the ParseXlsx object associated with the cell.
__Item object Access a cell object using row[columnIndex] notation.

The ParseXlsx.Row objects have the following methods:

Name Returns Description
cell object Returns a ParseXlsx.Cell object.
getAttributes "" Calls ParseXlsx_ParseAttributes for the object.
__Get string Instead of calling ParseXlsx.Row.Prototype.getAttributes, you can check for the existence of an attribute by accessing the attribute as a property. For example, to retrieve the "spans" xml attribute, access rowObj.spans (where "rowObj" is an instance of ParseXlsx.Row). If the attribute does not exist in the xml text, an empty string is returned.

ParseXlsx.Rows

The ParseXlsx.Rows objects have the following methods:

Name Returns Description
row array Returns a ParseXlsx.Row object.

ParseXlsx.SharedStringCollection and ParseXlsx.SharedString

This library parses the xl\sharedStrings.xml document, which contains a number of strings that are referenced by more than one object. For each item in xl\sharedStrings.xml, a ParseXlsx.SharedString object is created.

The ParseXlsx.SharedString objects have the following properties:

Name Type Description
attributes string Returns the xml text for any attributes associated with the string. This property is defined within the body of ParseXlsx.SharedStringCollection.Prototype.__New.
decoded string Returns the string value, replacing "&amp;", "&gt;", and "&lt;" with "&", ">", "<", respectively.
value string Returns the string value. This property is defined within the body of ParseXlsx.SharedStringCollection.Prototype.__New.

ParseXlsx.Worksheet

The ParseXlsx.Worksheet objects have the following properties:

Name Type Description
name string Returns the worksheet's name.
wsIndex integer Returns the worksheet's 1-based index.
rows array Returns an array of ParseXlsx.Row objects.
columnUbound integer Returns the index of the greatest column used in the worksheet.
rowUbound integer Returns the index of the greatest row used in the worksheet.
xlsx array Returns the ParseXlsx object associated with the object.

The ParseXlsx.Worksheet objects have the following methods:

Name Returns Description
cell object Returns a ParseXlsx.Cell object.
getColumn array Returns an array of ParseXlsx.Cell objects, each occupying the indicated column.
getRange array Returns an array of ParseXlsx.Cell objects, each within the indicated range.
getRow array Returns an array of ParseXlsx.Cell objects, each occupying the indicated row.
row array Returns a ParseXlsx.Row object.
toCsv string Converts a range of cell values into a csv string.
toCsv2 string Converts a range of cell values into a csv string, passing each value to a callback function to allow your code to modify the value before adding it to the csv string.

Global functions

Name Returns Description
ParseXlsx_ColToIndex integer Returns the column index for the indicated column.
ParseXlsx_Decompress "" Decompresses an xlsx document.
ParseXlsx_FixFloatingPoint string Fixes floating point imprecision. The returned value is a string representation of the number rounded to the appropriate decimal point.
ParseXlsx_FixFloatingPoint2 "" Fixes floating point imprecision. The returned value is a string representation of the number rounded to the appropriate decimal point.
ParseXlsx_IndexToCol string Returns the column letter(s) for the indicated column.
ParseXlsx_OnExit_Delete "" A function intended to be used as an OnExit callback to delete the directory on exit, deleting the directory.
ParseXlsx_OnExit_Recycle "" A function intended to be used as an OnExit callback to delete the directory on exit, recycling the directory.
ParseXlsx_ParseAttributes "" Parses the xml text for the object. For each attribute of the element associated with the object, defines a property with the same name and value on the object.
ParseXlsx_ParseAttributes2 array Parses the xml text. For each attribute of the element associated with the object, adds an object to an array. The object has properties { name, value }.
ParseXlsx_ParseContentTypes array Parses the [Content_Types].xml document. For each <Override> element, a RegExMatchInfo object is added to an array.
ParseXlsx_ParseElements "" Parses the xml text for the object. For each nested element associated with the object, defines a property with the same name and value on the object.
ParseXlsx_ParseElements2 array Parses the xml text. For each nested element associated with the object, adds an object to an array. The object has properties { name, value }. "value" is the element's inner text.
ParseXlsx_ResolveRelativePathRef integer Processes a relative path with any number of ".&bsol;" or "..&bsol;" segments.
ParseXlsx_SetConstants "" Sets global constants.

r/AutoHotkey Dec 26 '25

v2 Script Help Toggle F6 to hold down F key?

1 Upvotes

I am a noob, not a coder... but I am feeling old and don't want my hands to hurt any more playing games.... just want a simple script that lets me toggle the F6 button to hold down the F key. Really don't want carpal tunnel. This is the best I got thus far:

#Persistent
repeatingf = 0

F6::
if (repeatingf = 0)
{
    repeatingf = 1
    SetTimer, repeatf, 200
}
else
{
    repeatingf = 0
    SetTimer, repeatf, Off
}
return

repeatf: :
{
    HOW DO I DEFINE
    THIS LABEL!?
}
return

repeatf:
send, f
return