Last active
June 25, 2025 18:07
-
-
Save Lorenzo501/65843fb957ac2c53adeb9e29b3e91b85 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
This is a productivity enhancer plus some quality-of-life features that takes care of tedious tasks for you, saving time for what matters. Click on the tray icon to view hotkey info. | |
You'll find an embedded INI at the bottom of this script. However, you have to follow these two steps before you can run this script: | |
Download and unzip archive in `Lib` folder: https://github.com/thqby/winrt.ahk | |
Download raw file into `Lib` folder: https://github.com/Descolada/UIA-v2/blob/main/Lib/UIA.ahk | |
********** EIGHT-HOTKEY REMOTE ********** | |
Multi- and single-function hotkeys. | |
TIP: change the `Run` setting to `Maximize` in the properties wnd of the Paint shortcut (b/c the snip feature can open Paint in a small wnd, which will be remembered by the OS). | |
And go to `Settings > System > Focus assist` to turn off all `Automatic rules`, so that TrayTip notification banners can also be shown on the desktop wnd for example. | |
Pause = MEDIA CONTROL | |
Quick-press to play/pause, double-press to play next & triple-press to play previous. | |
Win+G = GOD MODE | |
Quick-press to trigger god mode (based on the active/existing context window, it'll run a destination). Double-press or long-press to add a context window and its destination. | |
Win+R = SMART RUN | |
Quick-press to open Run dialog with preferred input & double-press or long-press to configure preferred input. | |
Win+C = CALC/CTRL | |
Quick-press to open Calculator & double-press or long-press to display control list of active wnd in realtime. | |
Win+S = SNIP | |
Start snip to clipboard (editable through Paint once you click the TrayTip that disappears after 10 min). | |
PrtScn = SCREENSHOT+ | |
Quick-press to use native fn, double-press to save to desktop as well & long-press to merely save a clipboard image to desktop (does nothing if the clipboard has none). | |
XButton* = MOUSE MENU | |
Pressed down to show two-option menu & release to select. | |
MButton = CONCEAL CARET | |
Toggle caret in Notepad, VS Code or Visual Studio. | |
********** AUTOMATION FEATURES ********** | |
Open a network stream in VLC once you click `Copy Stream URL` in uTorrent. | |
Assist the Chrome extension `Tabs Outliner` by making sure it has finished saving before getting closed. | |
Auto-run Outlook minimized, this will allow you to see the unread emails badge for emails that you receive afterwards (so you might still want to check in the app at computer startup). | |
And also make the close btn minimize Outlook instead, and make each button work wherever you click on them (Outlook normally doesn't respond to clicks at the first line of pixels). | |
TIP: you can swap the badge file for another here: %LocalAppData%\Microsoft\Olk\cache | |
Upgrade VLC by opening videos of specific folder(s) with aspect ratio 16:9, changing default settings just once; to enable and fix features, and to prevent repetition and annoyances. | |
Automatically changing to the appropriate video output to fix the loop btn when HDR is not used, and to fix HDR subtitles when HDR or a network stream is used. And showing a close | |
btn in fullscreen when your cursor reaches the top-right corner, and making the fullscreen controller immovable, centered at the bottom. | |
TIP: you can restore the old settings by replacing this file: %AppData%\vlc\vlcrc, with the backup: `%LocalAppData%\Time Saver\vlcrc` | |
Activate the `Unsaved Changes` dialog when showed, on top of parent wnd (most importantly the dialog might otherwise not appear when you click the close btn via the preview wnd). | |
Unhide taskbar in specific fullscreen window(s), prevent the cursor from moving taskbar icons after locking the taskbar (to avoid moving them by mistake), and if LButton is pressed down | |
while the cursor is on a taskbar icon, moving the cursor far away will cancel the click. | |
Auto-close the annoying WinRAR and Software Ideas Modeler notifications. | |
Auto-backup uTorrent's resume.dat once a day into: `%LocalAppData%\Time Saver\uTorrent`, in case of corruption. This file is the list of torrents managed with the app and takes up only | |
a few hundred KB. | |
Unfocus the minimized uTorrent window at startup to prevent key presses from affecting it by accident. | |
TIP: move script to: %UserProfile%\Downloads\AutoHotkey, after that go to `Task Scheduler > Action > Import Task...` and then use the XML file (the task makes this feature faster). | |
Scroll namespace to top when nearby on VS edit control show event. | |
********** WINDOWS' BUILT-IN HOTKEYS ********** | |
Some native hotkeys that you should know about. | |
Win+D = Show desktop. | |
Win+; = Emoji menu (e.g. you could insert a checkmark into a txt file). | |
MButton = Cursor on URL opens website in new tab, cursor on app in taskbar opens new instance & cursor on scrollable area of website activates auto-scroll. | |
Alt+F4 = When on desktop it'll show the shutdown menu. | |
Ctrl+F = Open find wnd (any text that you select beforehand gets inserted into the Find wnd automatically). | |
F3 = Quickly find and select text that was previously entered in the find wnd but without having to open the Find wnd again. | |
Win+Alt+B = Toggle HDR (this feature has been made reliable with a custom hotkey). | |
Shift+Delete = Open a confirmation dialog for permanent deletion of anything that you've currently selected. | |
*/ | |
#Requires AutoHotkey 2.1-alpha.9 ; For _menu.Show(,, 0), guiControl.SetCue, struct, fn definition expression and `property ??` | |
#SingleInstance Off | |
A_DetectHiddenWindows := true | |
; To make the script work when an admin window is active | |
if (!InStr(A_AhkPath, "_UIA.exe")) | |
{ | |
; The non-UIA process will launch this script with UIA as long as there's no process with UIA already | |
if (!WinExist(A_ScriptFullPath " ahk_exe AutoHotkey64_UIA.exe")) | |
Run("*UIAccess " A_ScriptFullPath) | |
ExitApp() | |
} | |
else if (WinGetList(A_ScriptFullPath " ahk_exe AutoHotkey64_UIA.exe").Length > 1) | |
ExitApp() | |
A_DetectHiddenWindows := false | |
#Include <winrt.ahk-main\windows> | |
#Include <UIA> | |
Initialize() | |
; PowerShell HDR toggle pipe writer (replaces the Windows' built-in HDR toggle hotkey of Game Bar on demand, to make it reliable) | |
#!b:: | |
{ | |
if (!RegRead("HKEY_CURRENT_USER\System\GameConfigStore", "GameDVR_Enabled")) ; Detects disabled Game Bar because it shouldn't toggle it with both Game Bar and WindowsDisplayManager | |
Run("PowerShell.exe -Command `"" " | |
( | |
$pipeClient = New-Object System.IO.Pipes.NamedPipeClientStream('.', '\\.\pipe\HdrTogglePipe', [System.IO.Pipes.PipeDirection]::Out) | |
$pipeWriter = New-Object System.IO.StreamWriter($pipeClient) | |
$pipeClient.Connect() | |
$pipeWriter.WriteLine($true) | |
$pipeWriter.Flush() | |
)" "`"",, "Hide") | |
KeyWait("LWin") ; Prevents the Win+Alt+B hotkey from triggering again while it's still pressed down | |
} | |
*Pause::HandleHotkeyActivation(() => Send("{Media_Play_Pause}"), () => Send("{Media_Next}"), () => Send("{Media_Prev}")) | |
; This hotkey variant uses I1 so that it won't get triggered by I1 WinExist hotkey variants (AHK does not let WinActive hotkey variants get priority by default, so they take care of that) | |
#InputLevel 1 | |
#g::HandleGodModeHotkeyActivation(() => ShellNavigate("shell:::{ED7BA470-8E54-465E-825C-99712043E01C}")) | |
#InputLevel 0 ; Resets back to default | |
#r:: | |
{ | |
pressStartTime := A_TickCount | |
loop | |
{ | |
Sleep(10) | |
; Win+R Quick-Press & Double-Press. Opens the run dialog or the configuration dialog for preferred input | |
if (!GetKeyState("#", "P") && !GetKeyState("r", "P")) | |
{ | |
HandleHotkeyActivation(StartSmartRun, ConfigurePreferredSmartRunInput) | |
return | |
} | |
; Win+R Long-Press. Opens the configuration dialog for preferred input | |
if (A_TickCount - pressStartTime > 750) | |
{ | |
ConfigurePreferredSmartRunInput() | |
KeyWait("LWin") ; Prevents the Win+R hotkey from triggering again while it's still pressed down | |
return | |
} | |
} | |
StartSmartRun() | |
{ | |
Run("shell:::{2559a1f3-21d7-11d4-bdaf-00c04f60b9f0}") | |
WinWaitActive("Run ahk_class #32770") | |
if (preferredSmartRunInputSection := IniRead(iniPath, "PreferredSmartRunInput")) | |
try ControlSetText(preferredSmartRunInputSection, "Edit1"), Send("+{End}") | |
} | |
ConfigurePreferredSmartRunInput() | |
{ | |
preferredInput := InputBox("Enter the preferred input to show by default in the Run dialog. " | |
"Use %LocalAppData%—it's the one most people tend to use.`n`n" | |
"Pro Tip: QuickAssist for remote assistance (built into Windows).", "Preferred Input - Smart Run", "w397 h174", "%LocalAppData%") | |
if (preferredInput.Result = "OK") | |
{ | |
IniDelete(iniPath, "PreferredSmartRunInput") | |
IniWriteExistingContextWindows(preferredInput.Value, "[PreferredSmartRunInput]") ; Writes it below the godmode sections | |
} | |
} | |
} | |
#c:: | |
{ | |
pressStartTime := A_TickCount | |
loop | |
{ | |
Sleep(10) | |
; Win+C Quick-Press & Double-Press. Opens calculator or toggles the realtime control list | |
if (!GetKeyState("#", "P") && !GetKeyState("c", "P")) | |
{ | |
HandleHotkeyActivation(() => Run("Calc"), ToggleRealtimeControlList) | |
return | |
} | |
; Win+C Long-Press. This toggles the realtime control list | |
if (A_TickCount - pressStartTime > 750) | |
{ | |
ToggleRealtimeControlList() | |
KeyWait("LWin") ; Prevents the Win+C hotkey from triggering again while it's still pressed down | |
return | |
} | |
} | |
ToggleRealtimeControlList() | |
{ | |
static shouldShowRealtimeControlList := false | |
SetTimer(WatchActiveWindow, (shouldShowRealtimeControlList := !shouldShowRealtimeControlList) ? 200 : 0) | |
if (!shouldShowRealtimeControlList) | |
ToolTip() | |
WatchActiveWindow() | |
{ | |
if (isToolTipPrtScnShown) | |
return | |
try | |
{ | |
focusedHwnd := ControlGetFocus("A") | |
focusedClassNN := ControlGetClassNN(focusedHwnd) | |
controlList := "𝗖𝗼𝗻𝘁𝗿𝗼𝗹 𝗟𝗶𝘀𝘁 𝗢𝗳 𝗔𝗰𝘁𝗶𝘃𝗲 𝗪𝗶𝗻𝗱𝗼𝘄: " WinGetProcessName("A") | |
. "`n𝗙𝗼𝗰𝘂𝘀𝗲𝗱 𝗖𝗼𝗻𝘁𝗿𝗼𝗹: {𝗵𝗪𝗻𝗱: " focusedHwnd ", 𝗖𝗹𝗮𝘀𝘀𝗡𝗡: `"" focusedClassNN "`"}" | |
. "`n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`n" | |
for (classNN in WinGetControls("A")) | |
controlList .= classNN "`n" | |
if (controlList = "") | |
ToolTip("The active window has no controls.", toolTipAxes*) | |
else | |
ToolTip(ControlList, toolTipAxes*) | |
} | |
catch (TargetError) | |
ToolTip("No visible window is active.", toolTipAxes*) | |
} | |
} | |
} | |
#s:: | |
{ | |
static winEventHook := 0, clipImage | |
OnClipboardChange(HandleClipChanged) | |
Run("ms-screenclip:") | |
WinWait("ahk_exe ScreenClippingHost.exe") | |
WinWaitClose("ahk_exe ScreenClippingHost.exe") | |
OnClipboardChange(HandleClipChanged, 0) | |
if (!winEventHook) | |
winEventHook := HookEvent(EVENT_OBJECT_NAMECHANGE, HandleNameChangedSnipWinEvent) | |
; This (re)sets the timer to 10 minutes | |
SetTimer(DismissSnipNotification, -600000) | |
HandleClipChanged(*) => clipImage := ClipboardAll() | |
HandleNameChangedSnipWinEvent(hWinEventHook, event, hWnd, *) | |
{ | |
try | |
if (WinGetTitle(hWnd) = "Snip & Sketch" && WinGetClass(hWnd) = "ApplicationFrameWindow") | |
{ | |
DllCall("UnhookWinEvent", "Ptr", hWinEventHook) | |
winEventHook := 0 | |
WinSetTransparent(0, hWnd) | |
clipPrevious := ClipboardAll() | |
A_Clipboard := clipImage | |
WinWait(hWnd) | |
if (!DllCall("IsClipboardFormatAvailable", "UInt", CF_BITMAP := 2)) ; Detects clipboard without image | |
{ | |
Send("^c") ; Copies the image from Snip & Sketch in case the image didn't get saved in the clipboard | |
fallbackCopyActionStartTime := A_TickCount | |
; Waits until the clipboard has the image or times out after 2 seconds | |
while (!DllCall("IsClipboardFormatAvailable", "UInt", CF_BITMAP := 2)) | |
{ | |
if (A_TickCount - fallbackCopyActionStartTime > 2000) | |
{ | |
WinSetTransparent("Off") | |
TrayTip("This fallback timed out. Resolve!", "UNABLE TO COPY SNIP", 0x12) | |
SetTimer(HideTrayTip, -4000) | |
return | |
} | |
Sleep(10) | |
} | |
} | |
WinClose() | |
; Save clipboard image to temp folder to see its width and height (overwrites any that might've been saved in the past) | |
RunWait("PowerShell.exe -Command `"$img = get-clipboard -format image; $img.save('" A_Temp "\latestSnip.jpg')`"",, "Hide") | |
imageSize := GetImageSize(A_Temp "\latestSnip.jpg") | |
; The extra space is for the paint toolset ofcourse | |
rawPaintWinWidth := imageSize.Width + 27 | |
rawPaintWinHeight := imageSize.Height + 192 | |
; Initializing the paint wnd size (without the minimum, it will hide the paint toolset and without the maximum, it will go beyond the screen boundaries) | |
paintWinWidth := (rawPaintWinWidth >= 379 ? (rawPaintWinWidth <= A_ScreenWidth ? rawPaintWinWidth : A_ScreenWidth) : 379) | |
paintWinHeight := (rawPaintWinHeight >= 251 ? (rawPaintWinHeight <= A_ScreenHeight ? rawPaintWinHeight : A_ScreenHeight) : 251) | |
Run("mspaint.exe",, "Min", &paintPid) | |
WinWait("ahk_pid " paintPid) | |
WinSetTransparent(0) | |
Send("{Ctrl down}") | |
Sleep(250) | |
if (paintWinWidth = A_ScreenWidth && paintWinHeight = A_ScreenHeight) | |
{ | |
WinMaximize() ; Some images are so big that Paint needs to be shown in a maximized state | |
PasteImageAndRemoveRemainingWhiteSpace() ; `Shift` stays pressed | |
} | |
else | |
{ | |
WinActivate() | |
PasteImageAndRemoveRemainingWhiteSpace() ; `Shift` stays pressed | |
WinRestore() ; Unmaximize if it's maximized | |
WinMove((A_ScreenWidth / 2) - (paintWinWidth / 2), (A_ScreenHeight / 2) - (paintWinHeight / 2), paintWinWidth, paintWinHeight) ; Moves to center w/ new size | |
} | |
WinSetTransparent("Off") | |
Sleep(250) | |
Send("{Shift up}{Ctrl up}") | |
A_Clipboard := clipPrevious | |
} | |
} | |
; Make sure that Ctrl is down before using this fn, and release Shift and Ctrl after (using 250 ms delays might make it reliable) | |
PasteImageAndRemoveRemainingWhiteSpace() | |
{ | |
ControlSend("v") ; Pastes the image from clipboard | |
Sleep(250) | |
Send("{Shift down}") | |
Sleep(250) | |
WinActivate() | |
ControlSend("x") ; Removes any white space that might remain (crops the image) | |
} | |
GetImageSize(imagePath) | |
{ | |
static shellObj := ComObject("Shell.Application") | |
SplitPath(imagePath, &fileName, &fileDir) | |
folderObj := shellObj.Namespace(fileDir) | |
if (!folderItemObj := folderObj.ParseName(fileName)) | |
throw ValueError("The image path does not contain a (valid) file", -1, imagePath) | |
sizeArray := StrSplit(folderItemObj.ExtendedProperty("Dimensions"), " x ") | |
; Ad hoc object which allows you to see its properties with IntelliSense, usable w/ dot notation (image size w/o invisible characters) | |
return {Width: LTrim(sizeArray[1], Chr(8234)), Height: RTrim(sizeArray[2], Chr(8236))} | |
} | |
; This gets rid of the snip notification and its `winEventHook`, if it still exists | |
DismissSnipNotification(*) | |
{ | |
if (winEventHook) | |
{ | |
Windows.UI.Notifications.ToastNotificationManager.History.Clear("Microsoft.ScreenSketch_8wekyb3d8bbwe!App") | |
DllCall("UnhookWinEvent", "Ptr", winEventHook) | |
winEventHook := 0 | |
} | |
} | |
} | |
*PrintScreen:: | |
{ | |
static latestScreenshotTime := 0 | |
pressStartTime := A_TickCount | |
loop | |
{ | |
Sleep(10) | |
if (!GetKeyState("PrintScreen", "P")) | |
{ | |
; PrtScn Quick-Press. This takes a new screenshot | |
if (A_TickCount - latestScreenshotTime > 500) | |
{ | |
Send("{Blind}{PrintScreen}") ; Blind mode allows the native Win+PrtScn hotkey to save it in the `Screenshots` folder | |
latestScreenshotTime := A_TickCount | |
return | |
} | |
; PrtScn Double-Press. This saves the screenshot of the first press to the desktop | |
break | |
} | |
; PrtScn Long-Press. This saves any image that is currently in the clipboard to the desktop | |
if (A_TickCount - pressStartTime > 750) | |
{ | |
if (DllCall("IsClipboardFormatAvailable", "UInt", CF_BITMAP := 2)) ; Detects a clipboard image | |
break | |
ToolTip("No clipboard image detected.", toolTipAxes*) | |
global isToolTipPrtScnShown := true | |
KeyWait("PrintScreen") ; Prevents the PrtScn hotkey from triggering again while it's still pressed down and for tooltip dismissal | |
Tooltip() | |
global isToolTipPrtScnShown := false | |
return | |
} | |
} | |
uniqueImageName := LTrim(A_DD, "0") "-" LTrim(A_MM, "0") "-" A_YYYY " " A_Hour "." A_Min "." A_Sec | |
RunWait("PowerShell.exe -Command `"$img = get-clipboard -format image; $img.save('" A_Desktop "\" uniqueImageName ".jpg')`"",, "Hide") | |
TrayTip("CLIPBOARD IMAGE SAVED TO DESKTOP",, 0x10) | |
SetTimer(HideTrayTip, -4000) | |
KeyWait("PrintScreen") ; Prevents the PrtScn hotkey from triggering again while it's still pressed down | |
} | |
#HotIf (A_TitleMatchMode := 3, WinActive("Tabs Outliner ahk_exe chrome.exe")) ; TitleMatchMode 3 = exact title match (changes only the mode of the thread made for this specific #HotIf) | |
^x::Send("^c") ; Replaces cut with copy (otherwise you'll lose your tabs when more than one is cut) | |
^+c:: | |
F12:: | |
{ | |
A_DetectHiddenWindows := true ; Enables only the hidden window detection of the thread made for these two hotkeys | |
if (WinExist("DevTools - chrome-extension")) | |
WinShow(), WinActivate() | |
else | |
Send("{F12}") | |
} | |
#HotIf | |
*XButton1:: | |
*XButton2:: | |
{ | |
MouseGetPos(, &cursorY) | |
global unblockedCursorY := cursorY, unblockedStartCursorY := cursorY | |
mouseEventHook.Start() | |
SystemCursor("Hide") | |
_menu := Menu() | |
_menu.Add("Open Google", (*) => 0) | |
_menu.Add() ; seperator line | |
_menu.Add("Open Maps", (*) => 0) | |
_menu.Show(,, 0) | |
WinWait("ahk_class #32768 ahk_pid " DllCall("GetCurrentProcessId"),, 5) | |
ControlSend("{Down}") ; Highlights the first menu option | |
isFirstMenuOptionHighlighted := true | |
loop | |
{ | |
Sleep(50) | |
; Cancels when Esc has closed the menu and selects the highlighted menu option when the XButton* release has stopped the mouse hook | |
if (!WinExist()) | |
{ | |
SystemCursor("Show") | |
mouseEventHook.Stop() | |
return | |
} | |
else if (!mouseEventHook.ProcHandle) | |
{ | |
SystemCursor("Show") | |
ControlSend("{Enter}") | |
; Calling the relevant fn here b/c it's the fastest reliable way to do it (highlighting a different menu option isn't instant and meanwhile nothing will be highlighted) | |
if (isFirstMenuOptionHighlighted) | |
OpenGoogle() | |
else | |
OpenMaps() | |
return | |
} | |
if (isFirstMenuOptionHighlighted) | |
{ | |
if (unblockedCursorY - unblockedStartCursorY > 0) | |
{ | |
try ControlSend("{Down}") | |
isFirstMenuOptionHighlighted := false | |
MouseGetPos(, &cursorY) | |
global unblockedStartCursorY := cursorY | |
} | |
} | |
else if (unblockedCursorY - unblockedStartCursorY < 0) ; And when 2nd menu option is highlighted | |
{ | |
try ControlSend("{Up}") | |
isFirstMenuOptionHighlighted := true | |
MouseGetPos(, &cursorY) | |
global unblockedStartCursorY := cursorY | |
} | |
} | |
OpenGoogle(*) | |
{ | |
if (FileExist(A_ProgramFiles "\Google\Chrome\Application\chrome.exe")) | |
{ | |
if (ProcessExist("chrome.exe") || !(hasTabsOutlinerExtension := DirExist(EnvGet("LocalAppData") "\Google\Chrome\User Data\Default\Extensions\eggkanocgddhmamlbiijnphhppkpkmkl"))) | |
Run("chrome.exe https://www.google.com/ --start-maximized --disable-features=GlobalMediaControls") | |
else | |
{ | |
HookEvent(EVENT_OBJECT_NAMECHANGE, HandleNameChangedTabsOutlinerEvent) | |
Run("chrome.exe https://www.google.com/ --start-maximized --disable-features=GlobalMediaControls") | |
} | |
if (!WinWaitActive("Google - Google Chrome ahk_exe chrome.exe",, 3)) | |
return | |
FocusWebpage() ; `Shift` stays pressed | |
Send("{Shift up}") | |
} | |
else | |
Run("https://www.google.com/") | |
} | |
OpenMaps(*) | |
{ | |
if (FileExist(A_ProgramFiles "\Google\Chrome\Application\chrome.exe")) | |
{ | |
if (ProcessExist("chrome.exe") || !(hasTabsOutlinerExtension := DirExist(EnvGet("LocalAppData") "\Google\Chrome\User Data\Default\Extensions\eggkanocgddhmamlbiijnphhppkpkmkl"))) | |
Run("chrome.exe https://www.google.com/maps/ --start-maximized --disable-features=GlobalMediaControls") | |
else | |
{ | |
HookEvent(EVENT_OBJECT_NAMECHANGE, HandleNameChangedTabsOutlinerEvent) | |
Run("chrome.exe https://www.google.com/maps/ --start-maximized --disable-features=GlobalMediaControls") | |
} | |
if (WinExist("Google Maps - Google Chrome ahk_exe chrome.exe")) | |
Sleep(500) ; Waits for new tab in case there's already a Google Maps site open | |
if (!WinWaitActive("Google Maps - Google Chrome ahk_exe chrome.exe",, 3)) | |
return | |
FocusWebpage() ; `Shift` stays pressed | |
Sleep(500) ; Attempts to wait for new tab to become fully loaded | |
WinActivate() | |
ControlSend("{Tab}") ; Shift+Tab moves focus from the search box to the map | |
Send("{Ctrl down}") | |
Sleep(100) ; This is necessary to make `Ctrl` reliably modify sent keys | |
WinActivate() | |
ControlSend("l") ; Ctrl+Shift+L makes sure the map is zoomed-in onto your location | |
Send("{Ctrl up}{Shift up}") | |
} | |
else | |
Run("https://www.google.com/maps/") | |
} | |
; Argument can be Show, Hide, Toggle or Reload | |
SystemCursor(cmd) | |
{ | |
static visible := true, c := Map() | |
static sys_cursors := [32512, 32513, 32514, 32515, 32516, 32642, 32643, 32644, 32645, 32646, 32648, 32649, 32650] | |
if (cmd = "Reload" || !c.Count) ; Reload when requested or at first call | |
{ | |
for (i, id in sys_cursors) | |
{ | |
h_cursor := DllCall("LoadCursor", "Ptr", 0, "Ptr", id) | |
h_default := DllCall("CopyImage", "Ptr", h_cursor, "UInt", 2, "Int", 0, "Int", 0, "UInt", 0) | |
h_blank := DllCall("CreateCursor", "Ptr", 0, "Int", 0, "Int", 0, "Int", 32, "Int", 32, "Ptr", Buffer(32 * 4, 0xFF), "Ptr", Buffer(32 * 4, 0)) | |
c[id] := {Default: h_default, Blank: h_blank} | |
} | |
} | |
switch (cmd) | |
{ | |
case "Show": visible := true | |
case "Hide": visible := false | |
case "Toggle": visible := !visible | |
default: return | |
} | |
for (id, handles in c) | |
{ | |
h_cursor := DllCall("CopyImage", "Ptr", visible ? handles.Default : handles.Blank, "UInt", 2, "Int", 0, "Int", 0, "UInt", 0) | |
DllCall("SetSystemCursor", "Ptr", h_cursor, "UInt", id) | |
} | |
} | |
} | |
*XButton1 up:: | |
*XButton2 up::mouseEventHook.Stop() | |
; Removes caret from text editor by activating a wnd of the OS that you cannot see nor interact with, yet always exists (Microsoft Visual Studio uses a solution that leaves its wnd active) | |
#HotIf (WinActive("ahk_group TextEditor")) | |
*MButton::A_DetectHiddenWindows := true, WinActivate("ahk_class WorkerW") ; Enables only the hidden window detection of the thread made for this specific hotkey | |
; Restores caret to visible last active text editor, when a specific wnd of the OS is active, that you cannot see nor interact with, yet always exists (see hotkey variant directly above) | |
#HotIf (A_DetectHiddenWindows := true, WinActive("ahk_class WorkerW")) ; Enables only the hidden window detection of the thread made for this specific #HotIf | |
*MButton:: | |
{ | |
if (WinGetMinMax("ahk_group TextEditor") != -1) ; Detects maximized / restored down wnd (b/c it's only meant to restore the caret if the text editor is still visible) | |
GroupActivate("TextEditor", "R") | |
} | |
#HotIf (WinActive("Microsoft Visual Studio ahk_exe devenv.exe")) | |
*MButton:: | |
{ | |
if ((focusedElementSelection ?? false) && UIA.CompareElements(focusedElementLatest, UIA.GetFocusedElement())) | |
{ | |
; Moves caret back to where it was | |
try focusedElementSelection.Select() | |
global focusedElementLatest := unset, focusedElementSelection := unset | |
} | |
else | |
{ | |
; Moves caret to the start of the Edit control | |
global focusedElementLatest := UIA.GetFocusedElement(), focusedElementSelection := focusedElementLatest.SelectionRange | |
documentRange := focusedElementLatest.DocumentRange | |
documentRange.MoveEndpointByUnit(UIA.TextPatternRangeEndpoint.End, UIA.TextUnit.Document, -1) | |
documentRange.Select() | |
} | |
} | |
; Necessary to make A_ThisHotkey become one of these when pressed (HandleVisualStudioTextSelectionChangedEvent requires this) | |
~*LButton:: | |
~*RButton::return | |
#HotIf (WinActive("ahk_exe uTorrent.exe")) | |
~*RButton:: | |
{ | |
static status := 0 | |
if (!status) | |
OnClipboardChange(HandleClipChanged, status := 1) | |
HandleClipChanged(*) | |
{ | |
if (WinActive("ahk_exe uTorrent.exe")) | |
{ | |
HookEvent(EVENT_OBJECT_CREATE, HandleCreatedVlcEvent) | |
Send("+{F10}p{Enter}{Tab}"), Run(A_ProgramFiles "\VideoLAN\VLC\vlc.exe --fullscreen --aspect-ratio=16:9 " A_Clipboard) | |
} | |
OnClipboardChange(HandleClipChanged, status := 0) | |
HandleCreatedVlcEvent(hWinEventHook, event, hWnd, *) | |
{ | |
static isBeingShownAfterTorrentStartsUnforced := false, WS_EX_LAYERED := 0x00080000 | |
try | |
if (WinGetTitle(hWnd) = "VLC" && !isBeingShownAfterTorrentStartsUnforced) | |
{ | |
isBeingShownAfterTorrentStartsUnforced := true | |
try WinSetTransparent(0, hWnd) | |
try WinSetEnabled(false, "ahk_exe uTorrent.exe") | |
HotIf(MouseIsOverUTorrent) | |
Hotkey("LButton", ShowNetworkStreamMenu, "On") | |
Hotkey("RButton", ShowNetworkStreamMenu, "On") | |
DllCall("UnhookWinEvent", "Ptr", hWinEventHook) | |
Sleep(-1) ; Discards queued events that might remain | |
WinExist(hWnd) | |
; Waiting until VLC is full screen | |
try | |
loop | |
if (!isBeingShownAfterTorrentStartsUnforced) | |
return | |
else if (WinGetExStyle() = WS_EX_LAYERED) | |
break | |
Hotkey("LButton", "Off") | |
Hotkey("RButton", "Off") | |
try WinSetEnabled(true, "ahk_exe uTorrent.exe") | |
try WinActivate("ahk_exe uTorrent.exe") | |
try Send((ListViewGetContent("Count Focused", "SysListView324", WinExist("ahk_exe uTorrent.exe")) = 1 ? "{Down}{Up}" : "{Up}{Down}") "+{F10}s") | |
try WinActivate(hWnd) | |
try WinSetTransparent("Off", hWnd) | |
isBeingShownAfterTorrentStartsUnforced := false | |
} | |
MouseIsOverUTorrent(*) => MouseIsOver("ahk_exe uTorrent.exe") | |
; This makes it possible to abort the network stream when the download speed is too low | |
ShowNetworkStreamMenu(*) | |
{ | |
_menu := Menu() | |
_menu.Add("Abort Network Stream", AbortNetworkStream) | |
_menu.Show() | |
AbortNetworkStream(*) | |
{ | |
HotIf(MouseIsOverUTorrent) | |
Hotkey("LButton", "Off") | |
Hotkey("RButton", "Off") | |
WinClose("ahk_exe vlc.exe") | |
WinSetEnabled(true, "ahk_exe uTorrent.exe") | |
WinActivate("ahk_exe uTorrent.exe") | |
isBeingShownAfterTorrentStartsUnforced := false | |
} | |
} | |
} | |
} | |
} | |
#HotIf (MouseIsOver("Jump List for Google Chrome ahk_class Windows.UI.Core.CoreWindow")) | |
*LButton:: | |
{ | |
if (!ProcessExist("chrome.exe") && (hasTabsOutlinerExtension := DirExist(EnvGet("LocalAppData") "\Google\Chrome\User Data\Default\Extensions\eggkanocgddhmamlbiijnphhppkpkmkl"))) | |
{ | |
winEventHook := HookEvent(EVENT_OBJECT_NAMECHANGE, HandleNameChangedTabsOutlinerEvent) | |
SetTimer(() => DllCall("UnhookWinEvent", "Ptr", winEventHook), -20000) | |
} | |
Send("{LButton down}"), KeyWait("LButton"), Send("{LButton up}") | |
} | |
#HotIf (A_TitleMatchMode := 3, MouseIsOver("Tabs Outliner ahk_exe chrome.exe")) ; TitleMatchMode 3 = exact title match (changes only the mode of the thread made for this specific #HotIf) | |
*LButton:: | |
{ | |
WinGetClientPos(&tabsOutlinerX, &tabsOutlinerY, &tabsOutlinerWidth,, "Tabs Outliner ahk_exe chrome.exe") | |
MouseGetPos(&cursorX, &cursorY) | |
; Detects close btn click w/ save likely in progress and delays closing the wnd long enough for any save to finish | |
if (cursorX > tabsOutlinerX + tabsOutlinerWidth - 47 && cursorY < tabsOutlinerY + 31 && A_TickCount - latestTabsOutlinerAndChromeClickTime < 3200) | |
Sleep(3200 - (A_TickCount - latestTabsOutlinerAndChromeClickTime)), WinClose("Tabs Outliner ahk_exe chrome.exe") | |
else | |
{ | |
Send("{LButton down}"), KeyWait("LButton"), Send("{LButton up}") | |
global latestTabsOutlinerAndChromeClickTime := A_TickCount | |
} | |
} | |
#HotIf (WinActive("ahk_group Outlook")) | |
*LButton:: | |
{ | |
WinGetClientPos(&outlookX,, &outlookWidth) | |
MouseGetPos(&cursorX) | |
if (MouseIsOverOutlookButton()) ; Detects minimize-, restore- or close btn click | |
{ | |
KeyWait("LButton") | |
; Fixes an unresponsive click area (at the very top of the buttons) and prevents accidentally closing when you meant to minimize it | |
if (cursorX < outlookX + outlookWidth - 96 || cursorX > outlookX + outlookWidth - 49) ; Detects minimize- or close btn click | |
WinMinimize() | |
else if (WinGetMinMax() = 1) ; Detects maximized wnd with restore btn click | |
WinRestore() | |
else ; Detects restored down wnd with maximize btn click | |
WinMaximize() | |
} | |
else | |
Send("{LButton down}"), KeyWait("LButton"), Send("{LButton up}") | |
MouseIsOverOutlookButton() | |
{ | |
try return (MouseGetPos(,, &winId, &control), WinExist("ahk_group Outlook ahk_id " winId) && (control = "WebViewCaptionButton1" || control = "WebViewCaptionButtonDepr1")) | |
return false | |
} | |
} | |
#HotIf (WinActive("ahk_exe vlc.exe")) | |
LButton:: | |
{ | |
static fullscreenState := 2 | |
DllCall("Shell32\SHQueryUserNotificationState", "UInt*", &monitorState := 0) | |
if (monitorState != fullscreenState || monitorState = fullscreenState && WinExist("ahk_class Qt5QWindowToolTipSaveBits")) | |
Send("{LButton down}"), KeyWait("LButton"), Send("{LButton up}") | |
else | |
Send("{LButton}") | |
} | |
#HotIf (MouseIsOver("ahk_class Shell_TrayWnd")) | |
*LButton:: | |
{ | |
if (RegRead("HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced", "TaskbarSizeMove")) ; Detects unlocked taskbar | |
Send("{LButton down}"), KeyWait("LButton"), Send("{LButton up}") | |
else | |
{ | |
MouseGetPos(&cursorX, &cursorY) | |
global unblockedCursorX := cursorX, unblockedCursorY := cursorY, unblockedStartCursorX := cursorX, unblockedStartCursorY := cursorY | |
mouseEventHook.Start() | |
while (Abs(unblockedCursorX - unblockedStartCursorX) + Abs(unblockedCursorY - unblockedStartCursorY) < 1250) | |
if (KeyWait("LButton", "T0.05")) | |
{ | |
Send("{LButton}") | |
break | |
} | |
mouseEventHook.Stop() | |
} | |
} | |
;********** LIBRARY ********** | |
Initialize() | |
{ | |
TraySetIcon("netshell.dll", 151, true) | |
A_IconTip := "Time Saver" | |
A_CoordModeToolTip := "Screen" ; For the tooltip shown when the tray icon is clicked and also for the realtime controls tooltip | |
A_CoordModeMouse := "Screen" ; For the XButton hotkeys, and the LButton hotkey of the tabs outliner assist feature | |
A_WinDelay := -1 ; For the snip feature, HandleNameChangedThunderbirdEvent, HandleNameChangedTabsOutlinerEvent and the two different HandleCreatedVlcEvent functions | |
A_KeyDelay := -1 ; For the XButton hotkeys (ControlSend) and for Send b/c it reverts to the Event SendMode when another AHK script installs a native low-level keyboard/mouse hook | |
A_KeyDuration := 1 ; For the XButton hotkeys | |
EVENT_SYSTEM_FOREGROUND := 0x0003 | |
EVENT_SYSTEM_MINIMIZESTART := 0x0016 | |
global EVENT_OBJECT_CREATE := 0x8000 | |
EVENT_OBJECT_DESTROY := 0x8001 | |
global EVENT_OBJECT_SHOW := 0x8002 | |
EVENT_OBJECT_REORDER := 0x8004 | |
EVENT_OBJECT_SELECTION := 0x8006 | |
global EVENT_OBJECT_NAMECHANGE := 0x800C | |
global toolTipAxes := [A_ScreenWidth * 0.75, A_ScreenHeight], isToolTipPrtScnShown := false | |
global mouseEventHook := PointerDeviceHook("Move|LButton Down", HandleMouseEvent(eventInfo) | |
{ | |
; This uses the cursor position of the blocked mouse event to simulate the unblocked cursor position (e.g. to use `unblockedCursorX`, initialize it and `unblockedStartCursorX`) | |
try global unblockedCursorX += eventInfo.Pt.X - unblockedStartCursorX | |
try global unblockedCursorY += eventInfo.Pt.Y - unblockedStartCursorY | |
return true ; Blocks both interfering mouse actions | |
}) | |
global iniPath := A_ScriptFullPath | |
global hasGodmodeActiveContextWndTriggered := false | |
global latestTabsOutlinerAndChromeClickTime := 0 | |
GroupAdd("TextEditor", "ahk_exe notepad.exe") | |
GroupAdd("TextEditor", "- Visual Studio Code ahk_class Chrome_WidgetWin_1") | |
GroupAdd("Outlook", "E-mail ahk_class Olk Host") ; For backwards compatibility so you can still use older versions of Outlook | |
GroupAdd("Outlook", "E-mail ahk_class Outlook Host") | |
; Makes tray icon merely show hotkey info on click | |
A_TrayMenu.Delete() | |
OnMessage(0x404, HandleTrayIconClick) ; 0x404 = WM_USER msg number (which can be found w/ Microsoft Spy++) | |
; Win+G opens Game Bar by default in Windows, so this will disable it (press Win and type `Game Bar settings` to enable it again) | |
if (RegRead("HKEY_CURRENT_USER\System\GameConfigStore", "GameDVR_Enabled")) | |
{ | |
RegWrite(0, "REG_DWORD", "HKEY_CURRENT_USER\System\GameConfigStore", "GameDVR_Enabled") | |
RegWrite(0, "REG_DWORD", "HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\GameDVR", "AppCaptureEnabled") | |
} | |
; PowerShell load command for HDR toggle pipe reader and status toast | |
hdrTogglePowershellLoadCommand := " | |
( | |
try { $pipeServer = New-Object System.IO.Pipes.NamedPipeServerStream('\\.\pipe\HdrTogglePipe') } | |
catch { exit } # HDR toggle Powershell process exists already | |
$pipeReader = New-Object System.IO.StreamReader($pipeServer) | |
Import-Module WindowsDisplayManager | |
$primaryDisplay = GetPrimaryDisplay | |
$toastHdrStatusScriptBlock = [System.Management.Automation.ScriptBlock]::Create( | |
{ | |
param($hdrStatus) | |
$toastText01 = [Windows.UI.Notifications.ToastTemplateType, Windows.UI.Notifications, ContentType = WindowsRuntime]::ToastText01 | |
$templateHdr = [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime]::GetTemplateContent($toastText01) | |
$templateHdr.SelectSingleNode('//text[@id=1]').InnerText = $hdrStatus | |
$toastNotifier = [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('windows.immersivecontrolpanel_cw5n1h2txyewy!microsoft.windows.immersivecontrolpanel') | |
$toastNotifier.Show(($toastHdrStatus = [Windows.UI.Notifications.ToastNotification]::new($templateHdr))) | |
Start-Sleep -Seconds 4 | |
$toastNotifier.Hide($toastHdrStatus) | |
}) | |
while ($true) | |
{ | |
$pipeServer.WaitForConnection() | |
[void]$pipeReader.ReadLine() | |
if ($primaryDisplay.HdrInfo.HdrEnabled) | |
{ | |
$primaryDisplay.DisableHdr() | |
Start-Job -ScriptBlock $toastHdrStatusScriptBlock -ArgumentList 'HDR DISABLED' | |
} | |
else | |
{ | |
$primaryDisplay.EnableHdr() | |
Start-Job -ScriptBlock $toastHdrStatusScriptBlock -ArgumentList 'HDR ENABLED' | |
} | |
$pipeServer.Disconnect() | |
} | |
)" | |
; This silently installs the WindowsDisplayManager module one-time and loads the reliable PowerShell HDR toggle, for use with an on-demand hotkey (because Game Bar is unreliable) | |
try RegRead("HKEY_CURRENT_USER\SOFTWARE\Parzival\TimeSaver", "OneTimeWindowsDisplayManagerInstalled") ; Allows the catch scope to execute if the value name doesn't exist | |
catch | |
{ | |
RegWrite(1, "REG_DWORD", "HKEY_CURRENT_USER\SOFTWARE\Parzival\TimeSaver", "OneTimeWindowsDisplayManagerInstalled") | |
Run("PowerShell.exe -NoExit -ExecutionPolicy Bypass -Command `"" " | |
( | |
Install-PackageProvider -Name NuGet -Scope CurrentUser -Force # Necessary for the line below | |
Install-Module -Name WindowsDisplayManager -Scope CurrentUser -Force -AllowClobber`n | |
)" hdrTogglePowershellLoadCommand "`"",, "Hide") | |
} | |
else | |
Run("PowerShell.exe -NoExit -ExecutionPolicy Bypass -Command `"" hdrTogglePowershellLoadCommand "`"",, "Hide") | |
; Create [ActiveContextWindows] hotkeys with the INI. This gets done first so that these WinActive hotkey variants trigger instead of any WinExist ones that are created below and any | |
; that are created later when the user has added new one(s). If there are any WinExist hotkey variants that'd have triggered. Therefore, it won't matter whether I0 or I1 is used here, | |
; WinExist hotkey variants won't get to trigger these and obviously can't skip them either then | |
if (section := IniRead(iniPath, "ActiveContextWindows")) | |
{ | |
linesSplit := StrSplit(section, "`n") | |
for (iteratedLine in linesSplit) | |
((iteratedCommandData) => ( | |
HotIf((*) => WinActive(iteratedCommandData[1])), | |
Hotkey("#g", (*) => HandleGodModeHotkeyActivation(() => ShellNavigate(iteratedCommandData[2]))) | |
))(StrSplit(iteratedLine, "=")) | |
} | |
; Create [ExistingContextWindowPriority*] hotkeys with the INI. These use I1 to trigger any I0 WinActive hotkey variant instead that is created later when the user has added new one(s). | |
; If there are any of those WinActive hotkey variant(s) that'd trigger, otherwise it'd just execute its own God Mode action | |
try | |
loop | |
((iteratedCommandData) => ( | |
HotIf((*) => WinExist(iteratedCommandData[1])), | |
Hotkey("#g", (*) => ( | |
Send("#g"), Sleep(100), (hasGodmodeActiveContextWndTriggered ? Exit() : HandleGodModeHotkeyActivation(() => ShellNavigate(iteratedCommandData[2]))) | |
), "I1") | |
))(StrSplit(IniRead(iniPath, "ExistingContextWindowPriority" A_Index), "=")) | |
if (FileExist(A_ProgramFiles "\VideoLAN\VLC\vlc.exe")) | |
{ | |
; Resets fullscreen controller to the bottom center to avoid becoming stuck at the wrong position, b/c it's made immovable by a hotkey variant | |
IniWrite("", A_AppData "\vlc\vlc-qt-interface.ini", "FullScreen", "pos") | |
try RegRead("HKEY_CURRENT_USER\SOFTWARE\Parzival\TimeSaver", "OneTimeVlcSettingChangesExecuted") ; Allows the catch scope to execute if the value name doesn't exist | |
catch | |
{ | |
RegWrite(1, "REG_DWORD", "HKEY_CURRENT_USER\SOFTWARE\Parzival\TimeSaver", "OneTimeVlcSettingChangesExecuted") | |
FileCopy(A_AppData "\vlc\vlcrc", EnvGet("LocalAppData") "\Time Saver", true) ; Backing up old settings (only this INI is important enough to be backed up, the other is not) | |
changedVlcSettingsTrayTip := "" | |
try IniRead(A_AppData "\vlc\vlc-qt-interface.ini", "MainWindow", "FSCtoolbar") ; Allows the catch scope to execute if the key doesn't exist | |
catch | |
{ | |
IniWrite("`"0-2;64;3;1;4;64;37;64;38;64;8;65;9-4;35-4;34;`"", A_AppData "\vlc\vlc-qt-interface.ini", "MainWindow", "FSCtoolbar") | |
changedVlcSettingsTrayTip := "FscExtendedSettingsButton" | |
} | |
for (iteratedObj in | |
[ | |
{Section: "core", Key: "fullscreen" , Value: 1 , OccurredChange: "Fullscreen"}, | |
{Section: "core", Key: "custom-aspect-ratios", Value: "17:10" , OccurredChange: "CustomAspectRatios"}, | |
{Section: "core", Key: "stereo-mode" , Value: 1 , OccurredChange: "Stereo"}, | |
{Section: "core", Key: "volume-save" , Value: 0 , OccurredChange: "VolumeSaveOff"}, | |
{Section: "core", Key: "osd" , Value: 0 , OccurredChange: "OsdOff"}, | |
{Section: "core", Key: "spu" , Value: 0 , OccurredChange: "SubtitlesOff"}, | |
{Section: "core", Key: "vout" , Value: "direct3d9", OccurredChange: "HdrSubtitlesFix"}, | |
{Section: "qt" , Key: "qt-updates-notif" , Value: 0 , OccurredChange: "UpdatesNotifierOff"}, | |
{Section: "qt" , Key: "qt-system-tray" , Value: 0 , OccurredChange: "SystemTrayIconOff"} | |
]) | |
try | |
{ | |
IniRead(A_AppData "\vlc\vlcrc", iteratedObj.Section, "#" iteratedObj.Key) ; Allows the rest to execute if the commented-out default key exists | |
IniWrite("`n" iteratedObj.Key "=" iteratedObj.Value, A_AppData "\vlc\vlcrc", iteratedObj.Section, "#" iteratedObj.Key) | |
IniDelete(A_AppData "\vlc\vlcrc", iteratedObj.Section, "#" iteratedObj.Key) | |
changedVlcSettingsTrayTip .= ", " iteratedObj.OccurredChange | |
} | |
; Mentioning one-time setting changes in a persistent VLC notification | |
tnm := Windows.UI.Notifications.ToastNotificationManager | |
toastXml := tnm.GetTemplateContent("ToastText02") | |
toastXml.GetElementsByTagName("text").GetAt(0).InnerText := "New default settings make features usable, and prevent repetition and annoyances" | |
toastXml.GetElementsByTagName("text").GetAt(1).InnerText := changedVlcSettingsTrayTip | |
tnm.CreateToastNotifier("{6D809377-6AF0-444B-8957-A3773F02200E}\VideoLAN\VLC\vlc.exe").Show(Windows.UI.Notifications.ToastNotification(toastXml)) | |
} | |
HookEvent(EVENT_SYSTEM_FOREGROUND, HandleActivatedWindowExplorerEvent) | |
HookEvent(EVENT_OBJECT_SELECTION, HandleSelectionExplorerEvent) | |
} | |
; It'll simulate native non-fullscreen taskbar functionality in the specific fullscreen windows that you set here. Unforcefully allowing the taskbar to be shown when the cursor reaches | |
; the deactivated taskbar line (it won't activate the taskbar, so the taskbar retains its slide animation). Useful for non-video fullscreen windows | |
if (fullscreenWindowsUnhideTaskbarSection := IniRead(iniPath, "FullscreenWindowsUnhideTaskbar")) | |
{ | |
linesSplit := StrSplit(StrSplit(fullscreenWindowsUnhideTaskbarSection, "`n*/")[1], "`n") | |
for (iteratedLine in linesSplit) | |
GroupAdd("FullscreenWindows", iteratedLine) | |
} | |
tbl := CreateTaskbarList() | |
ProcessWaitClose("LogonUI.exe") ; Gotta wait until it closes before any of the WinEvent handlers activate a wnd (the logon screen will otherwise lose keyboard focus) | |
HookEvent(EVENT_OBJECT_DESTROY, HandleDestroyedChromeTabsOutlinerEvent) | |
outlookWinEventHook := HookEvent(EVENT_OBJECT_CREATE, HandleCreatedOutlookEvent) | |
HookEvent(EVENT_OBJECT_CREATE, HandleCreatedVlcEvent) | |
HookEvent(EVENT_OBJECT_REORDER, HandleReorderedVlcEvent) | |
HookEvent(EVENT_SYSTEM_FOREGROUND, HandleActivatedWindowVlcEvent) | |
HookEvent(EVENT_SYSTEM_FOREGROUND, HandleChangedFullscreenWinEvent) | |
HookEvent(EVENT_OBJECT_SHOW, HandleChangedFullscreenWinEvent) | |
HookEvent(EVENT_OBJECT_SHOW, HandleShowedUnsavedChangesDialogEvent) | |
HookEvent(EVENT_OBJECT_SHOW, HandleShowedNotificationEvent) | |
fullscreenVlcCloseButtonMouseEventHook := PointerDeviceHook("Move|LButton Down", HandleFullscreenVlcCloseButtonMouseEvent, true) | |
for (iteratedApp in ComObject("Shell.Application").NameSpace("shell:Appsfolder").Items) | |
if (iteratedApp.Name = "µTorrent") | |
{ | |
DirCreate(EnvGet("LocalAppData") "\Time Saver\uTorrent") ; Creating folder for daily uTorrent list backups | |
try FileCopy(A_AppData "\uTorrent\resume.dat", EnvGet("LocalAppData") "\Time Saver\uTorrent\" A_YYYY "_" A_YDay "_*.*") ; Backing up the uTorrent list (in case of corruption) | |
HookEvent(EVENT_SYSTEM_MINIMIZESTART, HandleMinimizeStartedUTorrentEvent) | |
} | |
if (ProcessExist("olk.exe")) ; Prevents opening multiple Outlook instances when Time Saver reloads (also no need to auto-run it minimized if the user had already opened it) | |
DllCall("UnhookWinEvent", "Ptr", outlookWinEventHook) | |
else | |
{ | |
A_DetectHiddenWindows := true | |
; This will wait on `Minimized Tabs Outliner.ahk` to have detected tabs outliner, if that script is being used as well (allows that script to keep a lower timeout and finish faster) | |
if (WinExist("Minimized Tabs Outliner.ahk")) | |
{ | |
GroupAdd("TabsOutliner", "_crx_eggkanocgddhmamlbiijnphhppkpkmkl") | |
GroupAdd("TabsOutliner", "Tabs Outliner ahk_exe chrome.exe") | |
WinWait("ahk_group TabsOutliner",, 30) | |
} | |
A_DetectHiddenWindows := false | |
Run("olk.exe",, "Max") ; Runs Outlook | |
} | |
ManageVisualStudioEventHandlers() ; Has a loop, so keep this at the bottom | |
HandleTrayIconClick(wParam, lParam, *) | |
{ | |
static WM_LBUTTONUP := 0x0202, WM_RBUTTONUP := 0x0205 | |
if (lParam = WM_LBUTTONUP || lParam = WM_RBUTTONUP) | |
{ | |
ToolTip("𝗛𝗢𝗧𝗞𝗘𝗬 𝗜𝗡𝗙𝗢`n" | |
"━━━━━━━━━━━━━━━━━━━━━━━━━━`n" | |
"𝗣𝗮𝘂𝘀𝗲 = MEDIA CONTROL`n" | |
"𝗪𝗶𝗻+𝗚 = GOD MODE`n" | |
"𝗪𝗶𝗻+𝗥 = SMART RUN`n" | |
"𝗪𝗶𝗻+𝗖 = CALC/CTRL`n" | |
"𝗪𝗶𝗻+𝗦 = SNIP`n" | |
"𝗣𝗿𝘁𝗦𝗰𝗻 = SCREENSHOT+`n" | |
"𝗫𝗕𝘂𝘁𝘁𝗼𝗻* = MOUSE MENU`n" | |
"𝗠𝗕𝘂𝘁𝘁𝗼𝗻 = CONCEAL CARET") | |
; This removes the tooltip whether modifiers are used or not, and allows hotkey variants to remain functional without them preventing the tooltip removal | |
(mouseEventHook := PointerDeviceHook("LButton Down|RButton Down", HandleMouseEvent)).Start() | |
HandleMouseEvent(*) => (ToolTip(), mouseEventHook.Stop()) | |
} | |
} | |
CreateTaskbarList() | |
{ | |
iid_ITaskbarList := "{56FDF342-FD6D-11d0-958A-006097C9A090}" ; Interface id (iid) | |
clsid_TaskbarList := "{56FDF344-FD6D-11d0-958A-006097C9A090}" ; Class id (clsid) | |
tbl := ComObject(clsid_TaskbarList, iid_ITaskbarList) ; Creates the TaskbarList object | |
loop (10) | |
{ | |
try ComCall(3, tbl) ; Calls tbl.HrInit(), which initializes the TaskbarList object | |
catch | |
Sleep(50) | |
else | |
break | |
} | |
return tbl | |
} | |
; Helps to make sure tabs outliner has finished saving the latest destroyed chrome wnd before tabs outliner gets closed | |
HandleDestroyedChromeTabsOutlinerEvent(hWinEventHook, event, hWnd, *) | |
{ | |
try | |
if (WinGetProcessName(hWnd) = "chrome.exe") | |
global latestTabsOutlinerAndChromeClickTime := A_TickCount | |
} | |
HandleCreatedOutlookEvent(hWinEventHook, event, hWnd, *) | |
{ | |
static outlookIsBeingLoaded := false | |
; Most windowing functions that are given a pure HWND will also work while it's hidden (dunno if it matters here but still) | |
try shouldExecute := ((winclass := WinGetClass(hWnd)) = "Olk Host" || winClass = "Outlook Host") && !outlookIsBeingLoaded | |
if (shouldExecute ?? false) | |
{ | |
outlookIsBeingLoaded := true | |
WinSetTransparent(0, hWnd) | |
ComCall(5, tbl, "Ptr", hWnd) ; Calls tbl.DeleteTab(winId), which deletes a tab from the TaskbarList | |
DllCall("UnhookWinEvent", "Ptr", hWinEventHook) | |
WinWait("- Outlook ahk_exe olk.exe") ; Minimum delay for the minimize to work | |
Sleep(2500) ; Extra delay to make sure that outlook has fully loaded before minimizing it and then outlook won't have to load once the user activates it | |
WinMinimize(hWnd) | |
WinSetTransparent("Off", hWnd) | |
ComCall(4, tbl, "Ptr", hWnd) ; Calls tbl.AddTab(winId), which adds a tab to the TaskbarList | |
} | |
} | |
HandleCreatedVlcEvent(hWinEventHook, event, hWnd, *) | |
{ | |
static isAttemptingToChangeControlAndAspectRatio := false | |
try | |
if (!isAttemptingToChangeControlAndAspectRatio && WinGetProcessName(hWnd) = "vlc.exe" && WinGetClass(hWnd) = "Qt5QWindowIcon" && DetermineIsNotChildWindow(hWnd)) | |
{ | |
isAttemptingToChangeControlAndAspectRatio := true | |
SetTimer(TryMoveContinuePlaybackButton, -1) | |
Sleep(400) ; Discards queued events that might remain, and allows VLC to both update its INI and fully load (necessary for the INI fn below to change VLC's aspect ratio) | |
if (DetermineIsPlayingFromAspectRatioVideosContextFolder()) | |
Send("a") | |
isAttemptingToChangeControlAndAspectRatio := false | |
} | |
; TitleMatchMode 3 = exact title match (changes only the mode of the thread made for this specific WinEvent handler) | |
DetermineIsNotChildWindow(hWnd) => (A_TitleMatchMode := 3, (winTitle := WinGetTitle(hWnd)) != "Building font cache" && winTitle != "vlc") | |
; `Continue Playback` btn must be moved to the left if it appears, because that spot in the top-right corner of the screen is meant for the custom fullscreen close btn feature | |
TryMoveContinuePlaybackButton() | |
{ | |
A_ControlDelay := -1 ; Changes only the control delay of the thread made for this specific WinEvent handler | |
; This GUI is for hiding the btn while it's not moveable | |
continuePlaybackButtonOverlayGui := Gui("+AlwaysOnTop -Caption") | |
continuePlaybackButtonOverlayGui.BackColor := "F0F0F0" | |
WinSetTransparent(0, continuePlaybackButtonOverlayGui) | |
WinSetExStyle("+0x08000000L", continuePlaybackButtonOverlayGui) ; Adds WS_EX_NOACTIVATE | |
continuePlaybackButtonOverlayGui.Show("x" A_ScreenWidth - 100 " y0 w100 h23 NoActivate") | |
; Allows the fn to continue if the btn appears within 200 ms where it would be when VLC is fullscreen | |
loop | |
{ | |
try | |
{ | |
ControlGetPos(, &controlY, &controlWidth,, "Qt5QWindowIcon9", "ahk_class Qt5QWindowIcon ahk_exe vlc.exe") | |
if (controlY = 0 && controlWidth = A_ScreenWidth) | |
break | |
} | |
if (A_Index = 20) | |
{ | |
WinClose(continuePlaybackButtonOverlayGui) | |
return | |
} | |
Sleep(10) | |
} | |
WinSetTransparent("Off", continuePlaybackButtonOverlayGui) ; Hides the btn as early as possible | |
; Moves the btn as early as possible | |
loop (5) | |
{ | |
Sleep(100) | |
ControlMove(,, A_ScreenWidth + 100,, "Qt5QWindowIcon9", "ahk_class Qt5QWindowIcon ahk_exe vlc.exe") ; Redrawing btn area w/ its bg | |
ControlMove(,, 380,, "Qt5QWindowIcon9", "ahk_class Qt5QWindowIcon ahk_exe vlc.exe") ; Moving btn to the left side of the screen, next to the info about it | |
} | |
WinClose(continuePlaybackButtonOverlayGui) | |
} | |
DetermineIsPlayingFromAspectRatioVideosContextFolder() | |
{ | |
playedMedia := IniRead(A_AppData "\vlc\vlc-qt-interface.ini", "RecentsMRL", "list") | |
; This detects a network stream from Popcorn Time to VLC (the uTorrent feature won't trigger a match, it uses the --aspect-ratio flag instead which is 100% reliable) | |
if (playedMedia ~= "^http://127.0.0.1:") ; The ^ requires the match to be at the start | |
return true | |
; https://www.autohotkey.com/docs/alpha/misc/RegEx-QuickRef.htm (Regular Expressions [RegEx] - Quick Reference) | |
loop parse (StrReplace(IniRead(A_ScriptFullPath, "AspectRatioVideosContextFolders"), " ", "%20")), "`n" | |
if (playedMedia ~= "^file:///" A_LoopField) ; The ^ requires the match to be at the start | |
return true | |
return false | |
} | |
} | |
; Changes VLC video output setting based on selection within Explorer, so that HDR subtitles work when playing HDR videos (default) or the loop btn when playing normal videos. Enables | |
; only the Critical mode of the thread made for these two WinEvent handlers (buffers hotkeys and timers until Critical is disabled or the thread finishes) | |
HandleActivatedWindowExplorerEvent(hWinEventHook, event, hWnd, *) => (Critical(-1), SetVlcVideoOutputForRelevantFeatureFix(hWnd)) | |
HandleSelectionExplorerEvent(hWinEventHook, event, hWnd, *) => (Critical(-1), SetVlcVideoOutputForRelevantFeatureFix(hWnd)) | |
SetVlcVideoOutputForRelevantFeatureFix(eventWinId) | |
{ | |
static isVlcLoopButtonFixInEffect := IniRead(A_AppData "\vlc\vlcrc", "core", "vout", "") != "direct3d9" | |
try isExplorerWnd := WinGetProcessName(eventWinId) = "explorer.exe" | |
detectedFolderPath := DetermineAnyShellFolderWinExist(, &selectedFileName, true) | |
if ((isExplorerWnd ?? false) && IsSet(selectedFileName)) | |
{ | |
if (InStr(selectedFileName, "HDR") || InStr(detectedFolderPath, "HDR")) | |
{ | |
if (isVlcLoopButtonFixInEffect) | |
{ | |
isVlcLoopButtonFixInEffect := false | |
IniWriteVlcVideoOutput("direct3d9") | |
} | |
} | |
else if (!isVlcLoopButtonFixInEffect) | |
{ | |
isVlcLoopButtonFixInEffect := true | |
IniWriteVlcVideoOutput("any") ; Sets `Automatic`, which is normally the default and results in Direct3D11 (fixes loop btn but breaks HDR subtitles) or newer to be used | |
} | |
} | |
else if (isVlcLoopButtonFixInEffect) | |
{ | |
isVlcLoopButtonFixInEffect := false | |
IniWriteVlcVideoOutput("direct3d9") ; Resets in case you start a network stream afterwards b/c then it must default to HDR compatibility | |
} | |
IniWriteVlcVideoOutput(value) | |
{ | |
try | |
{ | |
IniRead(A_AppData "\vlc\vlcrc", "core", "vout") ; Allows the rest to execute if the key exists | |
IniWrite(value, A_AppData "\vlc\vlcrc", "core", "vout") | |
return | |
} | |
try | |
{ | |
IniRead(A_AppData "\vlc\vlcrc", "core", "#vout") ; Allows the rest to execute if the commented-out default key exists | |
IniWrite("`nvout=" value, A_AppData "\vlc\vlcrc", "core", "#vout") | |
IniDelete(A_AppData "\vlc\vlcrc", "core", "#vout") | |
} | |
} | |
} | |
; Toggles custom fullscreen VLC close btn feature when VLC becomes active/inactive | |
HandleActivatedWindowVlcEvent(*) | |
{ | |
static isManagingCustomCloseButton := false | |
if (WinActive("ahk_exe vlc.exe")) | |
{ | |
if (!IsSet(fullscreenVlcCloseButtonGui) && !isManagingCustomCloseButton) | |
{ | |
; Loading custom fadeable close btn for in fullscreen VLC (which is almost identical to its native close btn) | |
isManagingCustomCloseButton := true | |
global fullscreenVlcCloseButtonGui := Gui("+AlwaysOnTop -Caption") | |
fullscreenVlcCloseButtonGui.BackColor := "E81123" | |
fullscreenVlcCloseButtonGui.SetFont("s11", "Comic Sans MS") | |
fullscreenVlcCloseButtonGui.AddText("x16 y0 cWhite Center", "X") ; Skipped .OnEvent("Click", ..) b/c it requires `x0 y0 w47` | |
WinSetTransparent(0, fullscreenVlcCloseButtonGui) | |
WinSetExStyle("+0x08000000L", fullscreenVlcCloseButtonGui) ; Adds WS_EX_NOACTIVATE | |
fullscreenVlcCloseButtonGui.Show("x" A_ScreenWidth - 47 " y0 w45 h21 NoActivate") | |
global fullscreenVlcCloseButtonFadeState := "FadedOut" | |
fullscreenVlcCloseButtonMouseEventHook.Start() | |
isManagingCustomCloseButton := false | |
} | |
} | |
else if (IsSet(fullscreenVlcCloseButtonGui) && !isManagingCustomCloseButton) | |
{ | |
isManagingCustomCloseButton := true | |
fullscreenVlcCloseButtonMouseEventHook.Stop() | |
WinClose(fullscreenVlcCloseButtonGui) | |
global fullscreenVlcCloseButtonGui := unset | |
isManagingCustomCloseButton := false | |
} | |
} | |
; Hides custom VLC close btn when the user goes out of fullscreen while it's visible | |
HandleReorderedVlcEvent(*) | |
{ | |
static fullscreenState := 2 | |
if (WinActive("ahk_exe vlc.exe")) | |
{ | |
DllCall("Shell32\SHQueryUserNotificationState", "UInt*", &monitorState := 0) | |
if (monitorState != fullscreenState && IsSet(fullscreenVlcCloseButtonFadeState) && fullscreenVlcCloseButtonFadeState = "FadedIn") | |
{ | |
try WinSetTransparent(0, fullscreenVlcCloseButtonGui) | |
global fullscreenVlcCloseButtonFadeState := "FadedOut" | |
} | |
} | |
} | |
HandleFullscreenVlcCloseButtonMouseEvent(eventInfo) | |
{ | |
static fullscreenState := 2, latestButtonUsageTime := 0 | |
DllCall("Shell32\SHQueryUserNotificationState", "UInt*", &monitorState := 0) | |
; Click is instant and desktop is fullscreen, so this MouseEvent handler shouldn't execute after the click closed VLC, before the WinEvent is handled (hence the 2nd condition) | |
if (monitorState != fullscreenState || !WinActive("ahk_exe vlc.exe")) | |
return | |
if (eventInfo.Pt.X > A_ScreenWidth - 48 && eventInfo.Pt.Y < 21) ; Hovering the close btn | |
{ | |
if (eventInfo.Action = "Move") | |
{ | |
if (fullscreenVlcCloseButtonFadeState = "FadedOut") | |
{ | |
global fullscreenVlcCloseButtonFadeState := "Fading" | |
; Using a timer allows the fade-in to finish while the close btn waits for release in the click state | |
SetTimer(FadeInVlcCloseButton() | |
{ | |
static opacity := 0 | |
try | |
{ | |
if ((opacity += 25) >= 255) | |
{ | |
WinSetTransparent("Off", fullscreenVlcCloseButtonGui), SetTimer(, 0), opacity := 0 | |
global fullscreenVlcCloseButtonFadeState := "FadedIn" | |
} | |
else | |
WinSetTransparent(opacity, fullscreenVlcCloseButtonGui) | |
} | |
catch | |
SetTimer(, 0), opacity := 0 | |
}, 10) | |
} | |
} | |
else if (latestButtonUsageTime != eventInfo.KeyUsageTime) ; And when LButton is being pressed down | |
{ | |
latestButtonUsageTime := eventInfo.KeyUsageTime | |
fullscreenVlcCloseButtonGui.BackColor := "F1707A" | |
fullscreenVlcCloseButtonMouseEventHook.Stop() | |
KeyWait("LButton") | |
try WinClose("ahk_class Qt5QWindowIcon ahk_exe vlc.exe") | |
global fullscreenVlcCloseButtonFadeState := "FadedOut" | |
} | |
} | |
else if (eventInfo.Action = "Move" && fullscreenVlcCloseButtonFadeState = "FadedIn") | |
{ | |
global fullscreenVlcCloseButtonFadeState := "Fading" | |
loop (255) | |
WinSetTransparent(255 - A_Index, fullscreenVlcCloseButtonGui), A_Index += 25, Sleep(10) | |
WinSetTransparent(0, fullscreenVlcCloseButtonGui) | |
global fullscreenVlcCloseButtonFadeState := "FadedOut" | |
} | |
} | |
HandleShowedUnsavedChangesDialogEvent(hWinEventHook, event, hWnd, *) | |
{ | |
if (WinExist("ahk_class #32770 ahk_id " hWnd)) | |
{ | |
try WinActivate(DllCall("GetParent", "UInt", hWnd)) | |
try WinActivate() | |
} | |
} | |
HandleChangedFullscreenWinEvent(hWinEventHook, event, hWnd, *) | |
{ | |
static fullscreenState := 2 | |
if (WinExist("ahk_group FullscreenWindows ahk_id " hWnd)) | |
{ | |
startTime := A_TickCount | |
; Repeats atleast 30 times to unmark as early as possible at an unknown repetition (the exact moment can't be determined, that would've been the only other way) | |
while (A_TickCount - startTime < 500) | |
{ | |
DllCall("Shell32\SHQueryUserNotificationState", "UInt*", &monitorState := 0) | |
if (monitorState != fullscreenState) | |
continue | |
MarkFullscreenWindow(hWnd, false) | |
DllCall("Sleep", "UInt", 10) ; This DllCall workaround prevents other events from being handled in the meantime | |
if (A_TickCount - startTime > 300) | |
return | |
} | |
} | |
; https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist2-markfullscreenwindow | |
MarkFullscreenWindow(hWnd := WinExist("A"), fullscreenStatusToSet := true) | |
{ | |
static tbl2 := CreateTaskbarList2() | |
ComCall(8, tbl2, "Ptr", hWnd, "Int", fullscreenStatusToSet) | |
CreateTaskbarList2() | |
{ | |
iid_ITaskbarList2 := "{602D4995-B13A-429B-A66E-1935E44F4317}" ; Interface id (iid) | |
return ComObjQuery(tbl, iid_ITaskbarList2) ; Queries the TaskbarList object to get the TaskbarList2 object | |
} | |
} | |
} | |
HandleShowedNotificationEvent(hWinEventHook, event, hWnd, *) | |
{ | |
; One WinRAR wnd has the title used below (permanently ofcourse) and has `ahk_class #32770`, while the other WinRAR wnd has the class used below and has temporary title `WinRAR`. | |
; The Software Ideas Modeler wnd has no unique title nor class at any point but it does have a control with the text used below | |
try | |
if | |
( | |
WinGetTitle(hWnd) = "Please purchase WinRAR license" || (WinGetClass(hWnd) = "RarReminder" && WinGetProcessName(hWnd) = "WinRAR.exe") || | |
ControlGetText("WindowsForms10.STATIC.app.0.2bf8098_r8_ad12", hWnd) = "Thank you for using Software Ideas Modeler!" | |
) | |
WinSetTransparent(0, hWnd), WinClose(hWnd) | |
} | |
; With Alt+Esc you can cycle open windows, but uTorrent gets that focus automatically at startup so this will unfocus it (e.g. prevents <Enter> from activating it) | |
HandleMinimizeStartedUTorrentEvent(hWinEventHook, event, hWnd, *) | |
{ | |
try | |
if (WinGetProcessName(hWnd) = "uTorrent.exe") | |
{ | |
DllCall("UnhookWinEvent", "Ptr", hWinEventHook) | |
if (WinGetOverlappedId() != hWnd) ; Updates last found wnd | |
WinActivate() ; Unfocuses uTorrent | |
} | |
} | |
ManageVisualStudioEventHandlers() | |
{ | |
static textSelectionChangedEventHandler := UIA.CreateAutomationEventHandler(HandleVisualStudioTextSelectionChangedEvent) | |
, structureChangedEventHandler := UIA.CreateStructureChangedEventHandler(HandleVisualStudioStructureChangedEvent) | |
, cacheRequest := UIA.CreateCacheRequest(["Type", "ClassName"],, UIA.TreeScope.Element) | |
loop | |
{ | |
WinWaitActive("Microsoft Visual Studio ahk_exe devenv.exe") | |
loop | |
{ | |
try | |
{ | |
visualStudioElement := UIA.ElementFromHandle("Microsoft Visual Studio ahk_exe devenv.exe") | |
break | |
} | |
catch | |
Sleep(100) | |
} | |
UIA.AddAutomationEventHandler(textSelectionChangedEventHandler, visualStudioElement, UIA.Event.Text_TextSelectionChanged) | |
UIA.AddStructureChangedEventHandler(structureChangedEventHandler, visualStudioElement,, cacheRequest) | |
WinWaitNotActive() ; To make it work with multiple VS windows, it'll remove the event handlers on deactivate, and event handlers are natively removed on close | |
try UIA.RemoveAutomationEventHandler(UIA.Event.Text_TextSelectionChanged, visualStudioElement, textSelectionChangedEventHandler) | |
UIA.RemoveStructureChangedEventHandler(structureChangedEventHandler, visualStudioElement) | |
} | |
; Reset caret hide state on relevant text selection | |
HandleVisualStudioTextSelectionChangedEvent(element, *) | |
{ | |
try | |
if (A_ThisHotkey != "*MButton" && UIA.CompareElements(focusedElementLatest, element)) | |
global focusedElementLatest := unset, focusedElementSelection := unset | |
} | |
; Auto-scroll namespace on relevant structure change | |
HandleVisualStudioStructureChangedEvent(sender, changeType, *) | |
{ | |
; Only StructureChangeType_ChildAdded is allowed to continue | |
if (changeType != 0) | |
return | |
if (sender.CachedType != UIA.Type.Pane || sender.CachedClassName != "ViewPresenter") | |
return | |
for (iteratedEditControl in sender.FindElements({Type: "Edit"})) | |
{ | |
try namespaceTextRange := iteratedEditControl.DocumentRange.FindText("namespace") | |
; The bounding rectangles obj exists only in the returned array when the text range is near the top, but also has properties (X, Y, W, H) that you can technically use | |
if (IsSet(namespaceTextRange) && namespaceTextRange.GetBoundingRectangles().Length = 1) | |
namespaceTextRange.ScrollIntoView(1) | |
} | |
} | |
} | |
} | |
HideTrayTip() | |
{ | |
TrayTip() ; Attempt to hide it the normal way | |
if (SubStr(A_OSVersion, 1, 3) = "10.") | |
A_IconHidden := true, Sleep(200), A_IconHidden := false | |
} | |
HandleGodModeHotkeyActivation(shellNavigateFnObj) | |
{ | |
static isActive := false ; Necessary because once the HandleNewHotkeyVariantDialog fn is called, it'll otherwise for some reason allow two godmode hotkey handlers to run simultaneously | |
; Prevents another godmode hotkey handler from being executed at the same time and also won't execute if the user has turned Game Bar back on, because it uses the same hotkey | |
if (isActive || RegRead("HKEY_CURRENT_USER\System\GameConfigStore", "GameDVR_Enabled")) | |
return | |
isActive := true | |
pressStartTime := A_TickCount | |
; Holding Win+G pressed down for 2sec will attempt to create the new hotkey variant dialog | |
loop | |
{ | |
Sleep(10) | |
if (!GetKeyState("#", "P") && !GetKeyState("g", "P")) | |
break | |
if (A_TickCount - pressStartTime > 2000) | |
{ | |
HandleNewHotkeyVariantDialog() | |
KeyWait("LWin") ; Prevents the godmode hotkey variant from triggering again while it's still pressed down (usually this alone is enough but here it also needs `isActive`) | |
isActive := false | |
return | |
} | |
} | |
HandleHotkeyActivation(shellNavigateFnObj, HandleNewHotkeyVariantDialog) | |
isActive := false | |
; This dialog allows any kind of destination to be used, you could for example save a special File Explorer window such as the Control Panel or the Recycle Bin as the Win+G destination. | |
; With this you could even change the default Win+G destination to the calendar app (configure apps folder to see ids), or https://youtube.com as destination to open YT in your browser | |
HandleNewHotkeyVariantDialog() | |
{ | |
static advancedInputDialogGui | |
if (!IsSet(advancedInputDialogGui)) | |
{ | |
; Here the advanced input dialog GUI for new hotkey variant gets created | |
advancedInputDialogGui := Gui("+AlwaysOnTop -SysMenu", "New Hotkey Variant - God Mode") | |
advancedInputDialogGui.BackColor := "White" | |
advancedInputDialogGui.AddText("y+19", "Win+G uses the context window to open the specified destination") | |
contextWindowEdit := advancedInputDialogGui.AddEdit("w375 h23 y+19") | |
contextWindowEdit.SetCue("Enter context wnd (e.g. ahk_exe explorer.exe)", true) | |
advancedInputDialogGui.AddButton("w160 h26", "Insert Overlapped Wnd").OnEvent("Click", InsertOverlappedWindow) | |
triggerOnActiveRadio := advancedInputDialogGui.AddRadio("Checked x+5 y86", "Trigger On Active") | |
advancedInputDialogGui.AddRadio("x+0", "Trigger On Existing") | |
destinationEdit := advancedInputDialogGui.AddEdit("w375 h23 x10 y+21") | |
destinationEdit.SetCue("Enter destination (e.g. shell:AppsFolder\App-User-Model-Id)", true) | |
advancedInputDialogGui.AddButton("w160 h26", "Insert Overlapped Shell Wnd").OnEvent("Click", InsertOverlappedShellWindow) | |
advancedInputDialogGui.AddButton("w88 h26 x+7", "Select Folder").OnEvent("Click", SelectFolder) | |
advancedInputDialogGui.AddButton("w88 h26 x205 y207", "OK").OnEvent("Click", ProcessUserInput) | |
advancedInputDialogGui.AddButton("w88 h26 x+7 y207", "Cancel").OnEvent("Click", (*) => (advancedInputDialogGui.Destroy(), advancedInputDialogGui := unset)) | |
advancedInputDialogGui.AddProgress("w395 h61 x0 y189 cF0F0F0", 100) | |
advancedInputDialogGui.AddButton("w88 h26 x205 y207", "OK") ; Gray bg btn overlay | |
advancedInputDialogGui.AddButton("w88 h26 x+7 y207", "Cancel") ; Gray bg btn overlay | |
advancedInputDialogGui.Show("w395 h250") | |
} | |
InsertOverlappedWindow(*) | |
{ | |
ControlSetText("ahk_exe " WinGetProcessName(WinGetOverlappedId()), contextWindowEdit) | |
ControlFocus(contextWindowEdit) | |
ControlSend("^a", contextWindowEdit) | |
} | |
InsertOverlappedShellWindow(*) | |
{ | |
detectedFolderPath := DetermineAnyShellFolderWinExist() | |
if (detectedFolderPath != "") | |
{ | |
ControlSetText(detectedFolderPath, destinationEdit) | |
ControlFocus(destinationEdit) | |
ControlSend("^a", destinationEdit) | |
} | |
} | |
SelectFolder(*) | |
{ | |
selectedFolder := FileSelect("D", "shell:MyComputerFolder\", "Select folder as destination to be reached with Win+G and context window") | |
if (selectedFolder != "") | |
{ | |
ControlSetText(selectedFolder, destinationEdit) | |
ControlFocus(destinationEdit) | |
ControlSend("^a", destinationEdit) | |
} | |
} | |
ProcessUserInput(*) | |
{ | |
contextWindow := ControlGetText(contextWindowEdit) | |
destination := ControlGetText(destinationEdit) | |
; These WinActive hotkey variants use I0 so that they can be triggered by any I1 WinExist hotkey variant (to gain priority over them) | |
if (ControlGetChecked(triggerOnActiveRadio)) | |
{ | |
IniWrite(destination, iniPath, "ActiveContextWindows", contextWindow) | |
global hasGodmodeActiveContextWndTriggered ; Allows the existing global reference to be passed to the WinActive hotkey variant below (can't place `global` in there directly) | |
HotIf((*) => WinActive(contextWindow)) | |
Hotkey("#g", (*) => ( | |
HandleGodModeHotkeyActivation(() => ShellNavigate(destination)), | |
hasGodmodeActiveContextWndTriggered := true, | |
SetTimer(() => hasGodmodeActiveContextWndTriggered := false, -100) | |
)) | |
} | |
else | |
{ | |
IniWriteExistingContextWindows(contextWindow "=" destination) | |
HotIf((*) => WinExist(contextWindow)) | |
Hotkey("#g", (*) => ( | |
Send("#g"), Sleep(100), (hasGodmodeActiveContextWndTriggered ? Exit() : HandleGodModeHotkeyActivation(() => ShellNavigate(destination))) | |
), "I1") | |
} | |
advancedInputDialogGui.Destroy() | |
advancedInputDialogGui := unset | |
} | |
} | |
} | |
; Detects single, double, and triple-presses of a hotkey. This allows a hotkey to perform a different operation depending on how many times you press it (convenient encapsulation only) | |
HandleHotkeyActivation(onePressActionFnObj, doublePressActionFnObj, triplePressActionFnObj?) | |
{ | |
static quickPressCount := 0, _executeMultiFunctionHotkeyAction | |
maxPressCount := IsSet(triplePressActionFnObj) ? 3 : 2 | |
if (quickPressCount + 1 < maxPressCount) | |
++quickPressCount | |
else | |
{ | |
quickPressCount := maxPressCount | |
return | |
} | |
if (!IsSet(_executeMultiFunctionHotkeyAction)) | |
_executeMultiFunctionHotkeyAction := ExecuteMultiFunctionHotkeyAction ; Allows timer updates | |
SetTimer(_executeMultiFunctionHotkeyAction, -500) | |
ExecuteMultiFunctionHotkeyAction() | |
{ | |
switch (quickPressCount) | |
{ | |
case 1: onePressActionFnObj() | |
case 2: doublePressActionFnObj() | |
case 3: triplePressActionFnObj() | |
} | |
quickPressCount := 0 | |
_executeMultiFunctionHotkeyAction := unset | |
} | |
} | |
WinGetOverlappedId() | |
{ | |
static WS_CAPTION := 0xC00000 ; title bar | |
for (iteratedId in WinGetList()) | |
if (WinExist(iteratedId) && !WinActive() && WinGetMinMax() != -1 && (WinGetClass() = "Progman" ? true : WinGetStyle() & WS_CAPTION) && WinGetTitle() != "") | |
return iteratedId | |
} | |
ShellNavigate(path) | |
{ | |
static shellObj := ComObject("Shell.Application") | |
if | |
( | |
InStr(path, "chrome.exe") && | |
!ProcessExist("chrome.exe") && | |
(hasTabsOutlinerExtension := DirExist(EnvGet("LocalAppData") "\Google\Chrome\User Data\Default\Extensions\eggkanocgddhmamlbiijnphhppkpkmkl")) | |
) | |
HookEvent(EVENT_OBJECT_NAMECHANGE, HandleNameChangedTabsOutlinerEvent) | |
; Shows search field after it runs an AHK site | |
if (InStr(path, "autohotkey.com")) | |
{ | |
Run(path) | |
Sleep(500) ; Waits for new tab in case there's already an AHK site open | |
if (!WinWaitActive("AutoHotkey v2",, 15)) | |
return | |
FocusWebpage() ; `Shift` stays pressed | |
Send("{Shift up}{Alt down}") | |
loop (5) | |
{ | |
Sleep(250) ; Attempts to wait for new tab to become fully loaded and 100ms of the first iteration is necessary to make `Alt` reliably modify sent keys | |
WinActivate() | |
ControlSend("s") | |
} | |
Send("{Alt up}") | |
return | |
} | |
; The first scope can change the active explorer window location (prevents unnecessarily opening another explorer window), but it can run all kinds of stuff just like the Run command | |
if ((activeWinId := WinActive("ahk_class CabinetWClass")) || (activeWinId := WinActive("ahk_class ExploreWClass"))) | |
{ | |
for (iteratedWindow in shellObj.Windows) | |
if ((activeWindow := iteratedWindow).Hwnd = activeWinId) | |
break | |
; Adding the shell: prefix to CLSIDs that do not have it already (to make them compatible with the Navigate2 fn) | |
if (SubStr(path, 1, 3) = "::{") | |
path := "shell:" path | |
activeWindow.Navigate2(path) | |
} | |
else | |
Run(path) | |
} | |
HandleNameChangedTabsOutlinerEvent(hWinEventHook, event, hWnd, *) | |
{ | |
static isBeingClosed := false | |
A_DetectHiddenWindows := true ; Enables only the hidden window detection of the thread made for this specific WinEvent handler (necessary to detect `Minimized Tabs Outliner.ahk`) | |
try | |
if (WinGetTitle(hWnd) = "_crx_eggkanocgddhmamlbiijnphhppkpkmkl" && !WinExist("Minimized Tabs Outliner.ahk") && !isBeingClosed) | |
{ | |
isBeingClosed := true | |
A_DetectHiddenWindows := false | |
try WinSetTransparent(0, hWnd) ; Gets rid of the close animation | |
try WinClose(hWnd) | |
DllCall("UnhookWinEvent", "Ptr", hWinEventHook) | |
Sleep(-1) ; Discards queued events that might remain | |
isBeingClosed := false | |
} | |
} | |
; Make sure to release Shift after using this fn | |
FocusWebpage() | |
{ | |
Send("{Ctrl down}") | |
Sleep(100) ; This is necessary to make `Ctrl` reliably modify sent keys | |
WinActivate() | |
ControlSend("l") ; Ctrl+L makes sure that the URL bar is definitely focused instead of maybe being focused (so that using Shift+F6 next will always move focus to the webpage) | |
Send("{Ctrl up}{Shift down}") | |
Sleep(100) ; This is necessary to make `Shift` reliably modify sent keys | |
WinActivate() | |
ControlSend("{F6}") ; Shift+F6 moves focus from the URL bar to the webpage so that it can receive a hotkey | |
} | |
; This writes a new section below [ExistingContextWindows] or the latest [ExistingContextWindowPriorityN] if there are any (the new section can be part of godmode but custom too!) | |
IniWriteExistingContextWindows(pairs, section?) | |
{ | |
loop | |
{ | |
try IniRead(iniPath, "ExistingContextWindowPriority" A_Index) ; Allows the catch scope to execute if the key doesn't exist | |
catch | |
{ | |
if (!IsSet(section)) | |
section := "[ExistingContextWindowPriority" A_Index "]" | |
IniWrite("`n" section (pairs = "" ? "" : "`n" pairs), iniPath, A_Index = 1 ? "ExistingContextWindows" : "ExistingContextWindowPriority" A_Index - 1, "[TemporarySection]") | |
IniDelete(iniPath, "TemporarySection") | |
break | |
} | |
} | |
} | |
DetermineAnyShellFolderWinExist(&locationName?, &selectedFileName?, shouldFirstDetermineDesktopActiveInstead := false) | |
{ | |
static shellObj := ComObject("Shell.Application") | |
isDesktopActive := false | |
try | |
if | |
( | |
(shouldFirstDetermineDesktopActiveInstead && (isDesktopActive := latestActiveWinId := WinActive("ahk_class Progman") || WinActive("ahk_class WorkerW"))) || | |
(latestActiveWinId := WinExist("ahk_class CabinetWClass") || WinExist("ahk_class ExploreWClass")) | |
) | |
{ | |
if (isDesktopActive) ; Gets skipped when deemed unrelevant (left on default) | |
latestActiveWindow := shellObj.Windows.FindWindowSW(0, 0, SWC_DESKTOP := 0x8, 0, SWFO_NEEDDISPATCH := 0x1) | |
else | |
for (iteratedWindow in shellObj.Windows) | |
if ((latestActiveWindow := iteratedWindow).Hwnd = latestActiveWinId) | |
break | |
locationName := latestActiveWindow.LocationName | |
for (iteratedSelectedFile in latestActiveWindow.Document.SelectedItems) | |
{ | |
selectedFileName := iteratedSelectedFile.Name | |
break | |
} | |
return latestActiveWindow.Document.Folder.Self.Path | |
} | |
} | |
class Point | |
{ | |
X: i32, Y: i32 | |
} | |
/* | |
Example usage: (pointerDeviceEventHook := PointerDeviceHook("Move|LButton Down|LButton Up", HandlePointerDeviceEvent)).Start() | |
/** @param {PointerDeviceHook.EventInfo} eventInfo Pointer device event info */` | |
HandlePointerDeviceEvent(eventInfo) | |
{ | |
ToolTip(Format(" | |
( | |
x{1}, y{2} | |
EventAction : {3} | |
EventKey : {4} | |
KeyUsageCount : {5} | |
KeyUsageTime : {6} | |
PressDuration : {7} | |
IsPenAction : {8} | |
)", eventInfo.Pt.X, eventInfo.Pt.Y, eventInfo.Action, eventInfo.Key, eventInfo.KeyUsageCount, eventInfo.KeyUsageTime, eventInfo.KeyPressDuration, eventInfo.IsPenAction)) | |
} | |
*/ | |
class PointerDeviceHook | |
{ | |
class InternalEventInfo | |
{ | |
Pt: Point | |
MouseData: i32 ; Necessary cast to `Int` | |
Flags: u32 | |
Time: u32 | |
ExtraInfo: uptr | |
} | |
class EventInfo | |
{ | |
Pt => this._IsPtAdaptive ? PointerDeviceHook.EventInfo.LatestPt : this._Pt | |
/** @prop {string} Action The event action */ | |
Action => this._IsActionAdaptive ? (PointerDeviceHook.EventInfo.LatestAction ?? this._Action) : this._Action | |
/** @prop {string} Key The event key */ | |
Key => PointerDeviceHook.EventInfo.IsKeyPressedDown ? PointerDeviceHook.EventInfo.RetainedKey : this._Key | |
/** @prop {integer} KeyUsageCount Count how many times a key gets pressed in a short time (KeyUsageCountTimeThreshold) */ | |
KeyUsageCount => PointerDeviceHook.EventInfo.IsKeyPressedDown ? PointerDeviceHook.EventInfo.RetainedKeyUsageCount : this._KeyUsageCount | |
/** @prop {string} KeyUsageTime The time when the event key was pressed, scrolled, or used for a pen action */ | |
KeyUsageTime => PointerDeviceHook.EventInfo.IsKeyPressedDown ? PointerDeviceHook.EventInfo.RetainedKeyUsageTime : this._KeyUsageTime | |
/** @prop {integer} KeyPressDuration The time elapsed since this key was pressed */ | |
KeyPressDuration => (this.KeyUsageTime ? A_TickCount - this.KeyUsageTime : 0) | |
/** @prop {integer} IsPenAction Whether or not the event is caused by a pen pointer device (not detected when using drawing tablet) */ | |
IsPenAction => PointerDeviceHook.EventInfo.IsKeyPressedDown ? PointerDeviceHook.EventInfo.RetainedIsPenAction : this._IsPenAction | |
/** @prop {boolean} IsKeyPressedDown The hook does not repeatedly trigger the down event while a key is pressed down, so those `onEvent` callbacks use timer & keeps relevant info */ | |
static IsKeyPressedDown := false | |
static __New() => this.LatestPt := Point() | |
__New() => this._Pt := Point() | |
} | |
/** @prop {Map} MsgList A list of message codes and actions (the commented wheel actions can also be used in the `PointerDeviceHook` constructor) */ | |
MsgList := Map( | |
512, "Move", | |
513, "LButton Down", | |
514, "LButton Up", | |
516, "RButton Down", | |
517, "RButton Up", | |
519, "MButton Down", | |
520, "MButton Up", | |
523, "XButton{:d} Down", | |
524, "XButton{:d} Up") | |
;——, "WheelUp" | |
;——, "WheelDown" | |
/** @prop {integer} ProcHandle SetWindowsHookEx */ | |
ProcHandle := 0 | |
/** @prop {Map} PriorKeyObjects The map logs the time of when keys were previously pressed, as well as the count (both saved inside an object which is associated with a key) */ | |
PriorKeyObjects := Map() | |
/** @prop {integer} KeyUsageCountTimeThreshold The time in which the usage of the event key gets counted, or reset when the time threshold is reached (milliseconds) */ | |
KeyUsageCountTimeThreshold := 500 | |
/** | |
* @param {string} [action="All"] Move, LButton Down, MButton Up, etc (the `MsgList` property shows all of them) | |
* @param {(hookObj) => Integer} onEvent This callback gets called on the event (to block mouse actions the callback must return true w/ default value `shouldMaintainResponsiveness`) | |
* @param {integer} [shouldMaintainResponsiveness=false] Default is False, causing it to call `onEvent` in this thread in favor of mouse-action-blocking capability (consider using timer | |
* in `onEvent`). Specify True to call it in a different thread if the `onEvent` code causes considerable mouse lag, as a result the Proc becomes incapable of blocking mouse actions | |
* @param {integer} [criticalMode=0] Default is Disabled Mode. A (positive) Enabled Mode value is the message check interval. Specifying -1 turns on Critical but disables message checks | |
*/ | |
__New(action := "All", onEvent := (*) => 0, shouldMaintainResponsiveness := false, criticalMode := 0) | |
{ | |
static WM_LBUTTONDOWN := 0x0201, WM_LBUTTONUP := 0x0202, WM_RBUTTONDOWN := 0x0204, WM_RBUTTONUP := 0x0205, WM_MBUTTONDOWN := 0x0207, WM_MBUTTONUP := 0x0208, WM_MOUSEWHEEL := 0x020A | |
, WM_XBUTTONDOWN := 0x020B, WM_XBUTTONUP := 0x020C, LLMHF_INJECTED := 0x00000001, MI_WP_SIGNATURE := 0xFF515700, SIGNATURE_MASK := 0xFFFFFF00 | |
; This callback gets executed when a pointer-device hook event occurs. The fast mode should be used only when it is known exactly which thread(s) this callback will be called from | |
this.InternalCallback := CallbackCreate(PointerDeviceProc, (criticalMode && criticalMode != "Off") ? "F" : unset, 3) | |
PointerDeviceProc(nCode, wParam, lParam) | |
{ | |
if (criticalMode = -1 || criticalMode) | |
Critical(criticalMode) | |
internalEventInfo := StructFromPtr(PointerDeviceHook.InternalEventInfo, lParam) | |
eventInfo := PointerDeviceHook.EventInfo() | |
; `internalEventInfo.MouseData` contains a reserved low-order word, which we don't need, and the high-order word is the leftmost 16 bits (bitshift removes the rest). | |
; `PointerDeviceHook.EventInfo.IsKeyPressedDown` is mainly set at this point so that the up event will also stop the repeated down event timer when the up action isn't hooked | |
switch (wParam) | |
{ | |
case WM_MOUSEWHEEL: eventInfo._Action := (internalEventInfo.MouseData >> 16) > 0 ? "WheelUp" : "WheelDown", PointerDeviceHook.EventInfo.IsKeyPressedDown := false | |
case WM_XBUTTONDOWN: eventInfo._Action := Format(this.MsgList[wParam], internalEventInfo.MouseData >> 16), PointerDeviceHook.EventInfo.IsKeyPressedDown := true | |
case WM_XBUTTONUP: eventInfo._Action := Format(this.MsgList[wParam], internalEventInfo.MouseData >> 16), PointerDeviceHook.EventInfo.IsKeyPressedDown := false | |
case WM_LBUTTONDOWN, WM_RBUTTONDOWN, WM_MBUTTONDOWN: eventInfo._Action := this.MsgList[wParam], PointerDeviceHook.EventInfo.IsKeyPressedDown := true | |
case WM_LBUTTONUP, WM_RBUTTONUP, WM_MBUTTONUP: eventInfo._Action := this.MsgList[wParam], PointerDeviceHook.EventInfo.IsKeyPressedDown := false | |
default: eventInfo._Action := this.MsgList.Has(wParam) ? this.MsgList[wParam] : 0 | |
} | |
if (nCode < 0 || (action != "All" && !InStr(action, eventInfo._Action))) | |
return CallNextHookEx(nCode, wParam, lParam) | |
eventInfo._IsPtAdaptive := eventInfo._IsActionAdaptive := false | |
eventInfo._KeyUsageTime := eventInfo._KeyUsageCount := eventInfo._Key := 0 | |
eventInfo._Pt.X := internalEventInfo.Pt.X | |
eventInfo._Pt.Y := internalEventInfo.Pt.Y | |
if (eventInfo._Action && eventInfo._Action != "Move") | |
{ | |
if (!this.PriorKeyObjects.Has(eventInfo._Action)) | |
this.PriorKeyObjects[eventInfo._Action] := {Time: internalEventInfo.Time, Count: 0} | |
priorKey := this.PriorKeyObjects[eventInfo._Action] | |
eventInfo._Key := eventInfo._Action | |
eventInfo._KeyUsageCount := priorKey.Count += internalEventInfo.Time - priorKey.Time < this.KeyUsageCountTimeThreshold ? 1 : -priorKey.Count + 1 ; Resets to 1 if false | |
eventInfo._KeyUsageTime := priorKey.Time := internalEventInfo.Time | |
eventInfo._IsPenAction := DetermineIsPenAction(internalEventInfo.ExtraInfo) | |
if (PointerDeviceHook.EventInfo.IsKeyPressedDown) | |
{ | |
keyDownEventInfo := eventInfo.Clone() | |
keyDownEventInfo._IsPtAdaptive := keyDownEventInfo._IsActionAdaptive := true | |
PointerDeviceHook.EventInfo.LatestPt.X := eventInfo._Pt.X | |
PointerDeviceHook.EventInfo.LatestPt.Y := eventInfo._Pt.Y | |
PointerDeviceHook.EventInfo.LatestAction := unset | |
PointerDeviceHook.EventInfo.RetainedKey := eventInfo._Key | |
PointerDeviceHook.EventInfo.RetainedKeyUsageCount := eventInfo._KeyUsageCount | |
PointerDeviceHook.EventInfo.RetainedKeyUsageTime := internalEventInfo.Time | |
PointerDeviceHook.EventInfo.RetainedIsPenAction := eventInfo._IsPenAction | |
SetTimer(() => (this.ProcHandle && PointerDeviceHook.EventInfo.IsKeyPressedDown ? onEvent(keyDownEventInfo) : SetTimer(, 0)), 50) | |
} | |
} | |
else if (PointerDeviceHook.EventInfo.IsKeyPressedDown) | |
{ | |
PointerDeviceHook.EventInfo.LatestPt.X := eventInfo._Pt.X | |
PointerDeviceHook.EventInfo.LatestPt.Y := eventInfo._Pt.Y | |
PointerDeviceHook.EventInfo.LatestAction := eventInfo._Action | |
} | |
else | |
eventInfo._IsPenAction := DetermineIsPenAction(internalEventInfo.ExtraInfo) | |
if (shouldMaintainResponsiveness) | |
{ | |
SetTimer(() => onEvent(eventInfo), -1) | |
return CallNextHookEx(nCode, wParam, lParam) | |
} | |
else | |
return onEvent(eventInfo) && !(internalEventInfo.Flags & LLMHF_INJECTED) ? true : CallNextHookEx(nCode, wParam, lParam) | |
} | |
DetermineIsPenAction(extraInfo) => ((extraInfo & SIGNATURE_MASK) = MI_WP_SIGNATURE) | |
CallNextHookEx(nCode, wParam, lParam) => DllCall("CallNextHookEx", "Ptr", 0, "Int", nCode, "UPtr", wParam, "Ptr", lParam) | |
} | |
/** Warning: Take caution when blocking mouse actions, you should not call this inside a mouse hotkey prefixed w/ the (~) tilde symbol (to make `Stop` unblock mouse actions properly) */ | |
Start() | |
{ | |
if (!this.ProcHandle) | |
this.ProcHandle := DllCall("SetWindowsHookEx", "Int", WH_MOUSE_LL := 14, "Ptr", this.InternalCallback, "Ptr", GetModuleHandle(), "UInt", 0) | |
GetModuleHandle() => DllCall("GetModuleHandle", "UInt", 0, "Ptr") | |
} | |
Stop() | |
{ | |
if (this.ProcHandle && DllCall("UnhookWindowsHookEx", "Ptr", this.ProcHandle)) | |
this.ProcHandle := 0 | |
} | |
__Delete() => (this.InternalCallback && (this.Stop(), CallbackFree(this.InternalCallback), this.InternalCallback := 0)) | |
} | |
MouseIsOver(winTitle, winText?, excludeTitle?, excludeText?) => (MouseGetPos(,, &winId), WinExist(winTitle " ahk_id " winId, winText?, excludeTitle?, excludeText?)) | |
HookEvent(event, fnObj, pid := 0) => DllCall("SetWinEventHook", "UInt", event, "UInt", event, "Ptr", 0, "Ptr", CallbackCreate(fnObj), "UInt", pid, "UInt", 0, "UInt", 0) | |
/* EXAMPLE | |
[_ActiveContextWindows] | |
ahk_exe Taskmgr.exe=services.msc | |
[_ExistingContextWindows] | |
[_ExistingContextWindowPriority1] | |
VOXEL WAR ahk_exe Unity.exe=shell:Profile\Documents\Unity projects\VOXEL WAR\Assets | |
[_ExistingContextWindowPriority2] | |
VOXEL WAR ahk_exe devenv.exe=shell:Profile\Documents\Unity projects\VOXEL WAR\Assets\Scripts | |
[_ExistingContextWindowPriority3] | |
ahk_exe Unity Hub.exe=shell:Profile\Documents\Unity projects | |
[_ExistingContextWindowPriority4] | |
ahk_exe Unity.exe=shell:Profile\Documents\Unity projects | |
[_ExistingContextWindowPriority5] | |
ahk_exe devenv.exe=shell:Profile\Documents\Unity projects | |
[_ExistingContextWindowPriority6] | |
ahk_exe notepad.exe=chrome.exe https://www.autohotkey.com/docs/alpha/ChangeLog.htm --start-maximized --disable-features=GlobalMediaControls | |
[_ExistingContextWindowPriority7] | |
- Visual Studio Code ahk_class Chrome_WidgetWin_1=chrome.exe https://www.autohotkey.com/docs/alpha/ChangeLog.htm --start-maximized --disable-features=GlobalMediaControls | |
[_PreferredSmartRunInput] | |
%LocalAppData% | |
[_AspectRatioVideosContextFolders] | |
C:/Users/loren/Downloads/Films | |
C:/Users/loren/AppData/Local/Temp/Popcorn Time | |
X:/ | |
D:/Films | |
E:/Opslag | |
[_FullscreenWindowsUnhideTaskbar] | |
Sourcetree ahk_exe SourceTree.exe | |
Software Ideas Modeler ahk_exe SoftwareIdeasModeler.exe | |
Popcorn-Time ahk_exe Popcorn-Time.exe | |
Epic Games Launcher ahk_exe EpicGamesLauncher.exe | |
GeForce Experience ahk_exe NVIDIA GeForce Experience.exe | |
*/ | |
/* Edit these variables yourself if necessary (e.g. to make it more specific you can add the ahk_class, and for the opposite effect you can replace the ahk_exe with an ahk_class or title) | |
[ActiveContextWindows] | |
[ExistingContextWindows] | |
[PreferredSmartRunInput] | |
[AspectRatioVideosContextFolders] | |
[FullscreenWindowsUnhideTaskbar] | |
*/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-16"?> | |
<Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"> | |
<Triggers> | |
<LogonTrigger> | |
<Enabled>true</Enabled> | |
</LogonTrigger> | |
</Triggers> | |
<Settings> | |
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries> | |
<IdleSettings> | |
<StopOnIdleEnd>false</StopOnIdleEnd> | |
</IdleSettings> | |
<AllowStartOnDemand>true</AllowStartOnDemand> | |
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit> | |
<Priority>5</Priority> | |
</Settings> | |
<Actions Context="Author"> | |
<Exec> | |
<Command>"%ProgramFiles%\AutoHotkey\v2\AutoHotkey.exe"</Command> | |
<Arguments>"%UserProfile%\Downloads\AutoHotkey\Time Saver.ahk"</Arguments> | |
<WorkingDirectory>%UserProfile%\Downloads\AutoHotkey</WorkingDirectory> | |
</Exec> | |
</Actions> | |
</Task> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Snipping Tool.ahk: