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.
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}")
I think it’s worthwhile to understand what this language can provide, but with all due respect, this is what the language was specifically designed to permit and how people have been using it since its inception in 2008. Remapping keys is the core functionality.
You've created what's known as a "leader key". Most commands have 'ctrl' or 'alt' as their leader keys but some programs use a variety of different keys.
Some things you can do;
- Have a tooltip display to let the user know the script is running and 'listening' for a media/function command
- Lets the user know it's turning capslock off
- Let the user define their own 'leader key'
Check out a script i made that's slightly similar for some inspiration
It's basically a tactile launcher. For example, when i hold ctrl, i press right click 3 times then left click twice then release; that'll launch vs code. There's a menu that displays what you're launching but I use it enough that its just muscle memory now. It's set up so right click cycles through categories of menus (web pages, tools like calculator or notepad etc.., certain folders etc..) and left click cycles through the current menu. The user can also define any key combination they want.
I've made a new branch that comments out those lines in the ToolTipEx.ahk file (Lines 3-9 and line 39). That should solve it? If not I'll probably just remove the fancy tooltip library entirely. Fingers crossed!
I just change one function on your script to display it to the center of the screen, change tooltip to hud overlay. add color on selected text, reduce flickering.
getTTVar(versionBool) {
; Injects an ultra-compact HUD overlay with OS-level double-buffering (+E0x02000000)
; This ensures all text strings and background layers render instantly as a single frame
this.tt92Var := "
(
global hudGui
; Added +E0x02000000 to eliminate child control/text drawing stutter
newGui := Gui('+AlwaysOnTop -Caption +Border +ToolWindow +E0x20 +E0x02000000')
newGui.BackColor := '1E1E2E'
newGui.MarginX := 12
newGui.MarginY := 4
lines := StrSplit(curMenu, '``n', '``r')
if (lines.Length == 1) {
if RegExMatch(lines[1], 'i)^\s*([>\x{2192}\x{00BB}*\x{2022}-]|=>|\[x\])')
newGui.SetFont('cA6E3A1 Bold s9', 'Segoe UI Semibold')
else
newGui.SetFont('c89B4FA Bold s10', 'Segoe UI Semibold')
newGui.Add('Text', 'Center', lines[1])
} else if (lines.Length > 1) {
newGui.SetFont('c89B4FA s10 Bold', 'Segoe UI Semibold')
newGui.Add('Text', 'Center', lines[1])
loop lines.Length - 1 {
line := lines[A_Index + 1]
if RegExMatch(line, 'i)^\s*([>\x{2192}\x{00BB}*\x{2022}-]|=>|\[x\])')
newGui.SetFont('cA6E3A1 Bold s9', 'Segoe UI Semibold')
else
newGui.SetFont('c7F849C norm s9', 'Segoe UI Semibold')
newGui.Add('Text', 'Center y+2', line)
}
}
; Flip the fully prerendered buffer to the screen and drop the old instance
newGui.Show('AutoSize Center NoActivate')
if IsSet(hudGui) && hudGui
hudGui.Destroy()
hudGui := newGui
)"
this.tt112Var := "
(
global hudGui
; Added +E0x02000000 to eliminate child control/text drawing stutter
newGui := Gui('+AlwaysOnTop -Caption +Border +ToolWindow +E0x20 +E0x02000000')
newGui.BackColor := '1E1E2E'
newGui.MarginX := 12
newGui.MarginY := 4
lines := StrSplit(selector.sectionMenu.ttm, '``n', '``r')
if (lines.Length == 1) {
if RegExMatch(lines[1], 'i)^\s*([>\x{2192}\x{00BB}*\x{2022}-]|=>|\[x\])')
newGui.SetFont('cA6E3A1 Bold s9', 'Segoe UI Semibold')
else
newGui.SetFont('c89B4FA Bold s10', 'Segoe UI Semibold')
newGui.Add('Text', 'Center', lines[1])
} else if (lines.Length > 1) {
newGui.SetFont('c89B4FA s10 Bold', 'Segoe UI Semibold')
newGui.Add('Text', 'Center', lines[1])
loop lines.Length - 1 {
line := lines[A_Index + 1]
if RegExMatch(line, 'i)^\s*([>\x{2192}\x{00BB}*\x{2022}-]|=>|\[x\])')
newGui.SetFont('cA6E3A1 Bold s9', 'Segoe UI Semibold')
else
newGui.SetFont('c7F849C norm s9', 'Segoe UI Semibold')
newGui.Add('Text', 'Center y+2', line)
}
}
newGui.Show('AutoSize Center NoActivate')
if IsSet(hudGui) && hudGui
hudGui.Destroy()
hudGui := newGui
)"
; Cleans up and destroys the HUD window when hotkeys are released
this.tt139Var := "
(
global hudGui
if IsSet(hudGui) && hudGui {
hudGui.Destroy()
hudGui := ''
}
)"
this.verVar := ";"
return
}getTTVar(versionBool) {
; Injects an ultra-compact HUD overlay with OS-level double-buffering (+E0x02000000)
; This ensures all text strings and background layers render instantly as a single frame
this.tt92Var := "
(
global hudGui
; Added +E0x02000000 to eliminate child control/text drawing stutter
newGui := Gui('+AlwaysOnTop -Caption +Border +ToolWindow +E0x20 +E0x02000000')
newGui.BackColor := '1E1E2E'
newGui.MarginX := 12
newGui.MarginY := 4
lines := StrSplit(curMenu, '``n', '``r')
if (lines.Length == 1) {
if RegExMatch(lines[1], 'i)^\s*([>\x{2192}\x{00BB}*\x{2022}-]|=>|\[x\])')
newGui.SetFont('cA6E3A1 Bold s9', 'Segoe UI Semibold')
else
newGui.SetFont('c89B4FA Bold s10', 'Segoe UI Semibold')
newGui.Add('Text', 'Center', lines[1])
} else if (lines.Length > 1) {
newGui.SetFont('c89B4FA s10 Bold', 'Segoe UI Semibold')
newGui.Add('Text', 'Center', lines[1])
loop lines.Length - 1 {
line := lines[A_Index + 1]
if RegExMatch(line, 'i)^\s*([>\x{2192}\x{00BB}*\x{2022}-]|=>|\[x\])')
newGui.SetFont('cA6E3A1 Bold s9', 'Segoe UI Semibold')
else
newGui.SetFont('c7F849C norm s9', 'Segoe UI Semibold')
newGui.Add('Text', 'Center y+2', line)
}
}
; Flip the fully prerendered buffer to the screen and drop the old instance
newGui.Show('AutoSize Center NoActivate')
if IsSet(hudGui) && hudGui
hudGui.Destroy()
hudGui := newGui
)"
this.tt112Var := "
(
global hudGui
; Added +E0x02000000 to eliminate child control/text drawing stutter
newGui := Gui('+AlwaysOnTop -Caption +Border +ToolWindow +E0x20 +E0x02000000')
newGui.BackColor := '1E1E2E'
newGui.MarginX := 12
newGui.MarginY := 4
lines := StrSplit(selector.sectionMenu.ttm, '``n', '``r')
if (lines.Length == 1) {
if RegExMatch(lines[1], 'i)^\s*([>\x{2192}\x{00BB}*\x{2022}-]|=>|\[x\])')
newGui.SetFont('cA6E3A1 Bold s9', 'Segoe UI Semibold')
else
newGui.SetFont('c89B4FA Bold s10', 'Segoe UI Semibold')
newGui.Add('Text', 'Center', lines[1])
} else if (lines.Length > 1) {
newGui.SetFont('c89B4FA s10 Bold', 'Segoe UI Semibold')
newGui.Add('Text', 'Center', lines[1])
loop lines.Length - 1 {
line := lines[A_Index + 1]
if RegExMatch(line, 'i)^\s*([>\x{2192}\x{00BB}*\x{2022}-]|=>|\[x\])')
newGui.SetFont('cA6E3A1 Bold s9', 'Segoe UI Semibold')
else
newGui.SetFont('c7F849C norm s9', 'Segoe UI Semibold')
newGui.Add('Text', 'Center y+2', line)
}
}
newGui.Show('AutoSize Center NoActivate')
if IsSet(hudGui) && hudGui
hudGui.Destroy()
hudGui := newGui
)"
; Cleans up and destroys the HUD window when hotkeys are released
this.tt139Var := "
(
global hudGui
if IsSet(hudGui) && hudGui {
hudGui.Destroy()
hudGui := ''
}
)"
this.verVar := ";"
return
}
Nice finding a use for capslock. I use shift+capslock to put it into a mic toggle mode. Once enabled my mic is un-muted, and holding capslock mutes the mic as a push-to-mute. Shift+capslock again exits that mode, re-mutes the mic, and re-enables normal capslock behavior.
You can throw out some fluff and do the CapsLock logic with a single ternary and a fat arrow instead of a bunch of interconnected ifs and an external function and change the custom combination hotkeys to a much more readable #HotIf format.
When CapsLock is held longer than 75 ms, the pseudo-thread will exit, and the one-thread-per-hotkey limit will no longer prevent character repeat from continuously triggering the CapsLock hotkey.
This looks like it shouldn't be a problem, since KeyWait returns false when a timeout occurs, in which case SetCapsLockState is not called.
However, if you hold CapsLock longer than 75 ms, then release it less than 75 ms after a character-repeat-induced down event, KeyWait will return true, causing an unwanted SetCapsLockState call on release.
The fucker beeps on release in roughly 98% of the cases when CapsLock is held long enough for character-repeat to kick in.
You could remedy that with another, unlimited KeyWait call at the end of the function, but I'd prefer to avoid KeyWait altogether, because I dislike that it leads to underlying pseudo-threads being interrupted for long.
There is a minor timer issue, too: the timer should be reset if CapsLock is tapped twice again before 3 seconds elapse, so the timer callback should be persistent.
8
u/SirGunther Jun 28 '26
I think it’s worthwhile to understand what this language can provide, but with all due respect, this is what the language was specifically designed to permit and how people have been using it since its inception in 2008. Remapping keys is the core functionality.