Hotkeys and hotstrings problem :
Now It seems to be working well and the flashing problem no longer occurs But I have this script for test only :
#SingleInstance Force
#Requires AutoHotkey v2.0
SendMode("Input")
Persistent
SetWorkingDir(A_ScriptDir) ; Ensures a consistent starting directory
SetTitleMatchMode(2)
ProcessSetPriority("High")
SetTimer test, 1000
test() {
WinWaitActive("ahk_class Notepad")
WinWaitNotActive("ahk_class Notepad")
SoundBeep(1000)
}
; =================================================================
; used in SetWinEventHook (for eventMin and eventMax)
; check here for additional events: https://learn.microsoft.com/en-us/windows/win32/winauto/event-constants
global EVENT_SYSTEM_ALERT := 0x2 ; 2 - alert has been generated
global EVENT_SYSTEM_FOREGROUND := 0x3 ; 3 - foreground window changed
global EVENT_SYSTEM_MENUSTART := 0x4 ; 4 - menu-bar menu opened
global EVENT_SYSTEM_MENUEND := 0x5 ; 5 - menu-bar menu closed
global EVENT_SYSTEM_MENUPOPUPSTART := 0x6 ; 6 - pop-up menu opened
global EVENT_SYSTEM_MENUPOPUPEND := 0x7 ; 7 - pop-up menu closed
global EVENT_SYSTEM_CAPTURESTART := 0x8 ; 8 - mouse click start
global EVENT_SYSTEM_CAPTUREEND := 0x9 ; 9 - mouse click end
global EVENT_SYSTEM_MOVESIZESTART := 0xA ; 10 - window move/resize start
global EVENT_SYSTEM_MOVESIZEEND := 0xB ; 11 - window move/resize end
global EVENT_SYSTEM_SCROLLINGSTART := 0x12 ; 18 - scrolling started on a scrol bar
global EVENT_SYSTEM_SCROLLINGEND := 0x13 ; 19 - scrolling ended on a scrol bar
global EVENT_SYSTEM_SWITCHSTART := 0x14 ; 20 - alt+tab pressed
global EVENT_SYSTEM_SWITCHEND := 0x15 ; 21 - alt+tab released
global EVENT_SYSTEM_MINIMIZESTART := 0x16 ; 22 - window minimized
global EVENT_SYSTEM_MINIMIZEEND := 0x17 ; 23 - window restored from minimize
global WINDOW_DRAG_STARTED := 0x19 ; 25 - window drag start (documentation not found for this)
global EVENT_SYSTEM_DESKTOPSWITCH := 0x20 ; 32 - active desktop has switched
global EVENT_OBJECT_CREATE := 0x8000 ; 32768 - an object has been created
global EVENT_OBJECT_DESTROY := 0x8001 ; 32769 - an object has been destroyed
global EVENT_OBJECT_SHOW := 0x8002 ; 32770 - a hidden object is shown
global EVENT_OBJECT_LOCATIONCHANGE := 0x800B ; 32779 - an object has changed location, shape, or size
global EVENT_OBJECT_NAMECHANGE := 0x800C ; 32780 - an object's Name property has changed
global EVENT_OBJECT_VALUECHANGE := 0x800E ; 32782 - an object's Value property has changed
global EVENT_OBJECT_PARENTCHANGE := 0x800F ; 32783 - an object has a new parent object
global EVENT_OBJECT_CLOAKED := 0x8017 ; 32791 - a window is cloaked. A cloaked window still exists, but is invisible to the user.
global EVENT_OBJECT_UNCLOAKED := 0x8018 ; 32792 - a window is uncloaked. A cloaked window still exists, but is invisible to the user.
; used in SetWinEventHook (for dwFlags)
global WINEVENT_OUTOFCONTEXT := 0x0000 ; Events are ASYNC
global WINEVENT_SKIPOWNTHREAD := 0x0001 ; Don't call back for events generated by this thread
global WINEVENT_SKIPOWNPROCESS := 0x0002 ; Don't call back for events generated by threads of this process
; used with the callback function of SetWinEventHook (for idObject and idChild)
global OBJID_WINDOW := 0x00000000 ; 0
global OBJID_SYSMENU := 0xFFFFFFFF ; -1
global OBJID_TITLEBAR := 0xFFFFFFFE ; -2
global OBJID_MENU := 0xFFFFFFFD ; -3
global OBJID_CLIENT := 0xFFFFFFFC ; -4
global OBJID_VSCROLL := 0xFFFFFFFB ; -5
global OBJID_HSCROLL := 0xFFFFFFFA ; -6
global OBJID_SIZEGRIP := 0xFFFFFFF9 ; -7
global OBJID_CARET := 0xFFFFFFF8 ; -8
global OBJID_CURSOR := 0xFFFFFFF7 ; -9
global OBJID_ALERT := 0xFFFFFFF6 ; -10
global OBJID_SOUND := 0xFFFFFFF5 ; -11
global OBJID_QUERYCLASSNAMEIDX := 0xFFFFFFF4 ; -12
global OBJID_NATIVEOM := 0xFFFFFFF3 ; -13
global CHILDID_SELF := 0
; saved for releasing resources appropriately
global hookProcAdr := ""
global hWinEventHooks := []
; list of windows for which to create a border
; Title is what will be matched with WinActive(Title)
; if the final list entry's Title is "A", it will make the current active window be the "fallback" if none of the others match
global rectList := [{
Title: "ahk_class PotPlayer64",
Rect: DrawRect(2, 'green', 0)
}, {
Title: "ahk_exe vivaldi.exe",
Rect: DrawRect(2, 'yellow', 2)
}, {
Title: "ahk_exe Reverso.exe",
Rect: DrawRect(2, 'red', 0)
}, {
; Title: "Program Manager",
; Rect: DrawRect(2, '33cc33', 2)
; }, {
Title: "- Notepad",
Rect: DrawRect(4, 'green', 1)
}, {
Title: "Microsoft Store",
Rect: DrawRect(2, 'red', 3)
}, {
Title: "A",
Rect: DrawRect()
}
]
class DrawRect extends Gui {
__New(border_thickness := "3", border_color := "red", offset := 0) {
super.__New("+AlwaysOnTop -Caption +ToolWindow", "GUI4Boarder")
super.BackColor := border_color
super.Opt("+E0x08080028") ; WS_EX_NOACTIVATE|WS_EX_LAYERED|WS_EX_TRANSPARENT|WS_EX_TOPMOST
DllCall("SetLayeredWindowAttributes", "Ptr", super.Hwnd, "Ptr", 0, "UChar", 255, "UInt", 2, "Int") ; allows this "Layered Window" to be visible and makes it fully opaque (i.e. 255)
this.offset := offset
this.parentHwnd := 0
this.x := 0
this.y := 0
this.w := 0
this.h := 0
this.outerX := offset
this.outerY := offset
this.innerX := border_thickness + offset
this.innerY := border_thickness + offset
this.IsShowing := false
}
UpdatePosition(hwnd) {
this.parentHwnd := hwnd
super.Opt("+Owner" this.parentHwnd)
WinGetPos(&x, &y, &w, &h, this.parentHwnd)
if (x == this.x && y == this.y && w == this.w && h == this.h)
return false ; position is the same as the last time this was checked
this.x := x
this.y := y
this.w := w
this.h := h
return true
}
Show() {
outerX2 := this.w - this.outerX
outerY2 := this.h - this.outerY
innerX2 := this.w - this.innerX
innerY2 := this.h - this.innerY
WinSetRegion(this.outerX "-" this.outerY " " outerX2 "-" this.outerY " " outerX2 "-" outerY2 " " this.outerX "-" outerY2 " " this.outerX "-" this.outerY " " this.innerX "-" this.innerY " " innerX2 "-" this.innerY " " innerX2 "-" innerY2 " " this.innerX "-" innerY2 " " this.innerX "-" this.innerY, super.Hwnd)
; super.Show("w" . this.w . " h" . this.h . " x" . this.x . " y" . this.y . " NoActivate")
; Similar to super.OptShow("+E0x800A8"whxy) but also places it at the top of the z-order
DllCall("SetWindowPos", "Ptr", super.Hwnd, "Ptr", 0, "Int", this.x, "Int", this.y, "Int", this.w, "Int", this.h, "UInt", 0x4250, "Int") ; SWP_NOACTIVATE|SWP_SHOWWINDOW|SWP_NOOWNERZORDER|SWP_ASYNCWINDOWPOS (0x10|0x40|0x200|0x4000)
this.IsShowing := true
}
Hide() {
super.Hide()
this.IsShowing := false
}
}
ShowRect(hWinEventHook, Event, hWnd, idObject := 0, idChild := 0, dwEventThread := 0, dwmsEventTime := 0) {
Critical -1 ; turns on Critical (making the thread uninterruptible) but disables message checks (via -1)
; ignore events for objects that are not windows and events for child objects
if (idObject !== OBJID_WINDOW || idChild !== CHILDID_SELF)
return
for _, value in rectList {
; the latter two WinActive checks will exclude the Windows Desktop itself from matching but you can remove them if you aren't using a WinActive("A") gui
if ((winHwnd := WinActive(value.Title)) && !WinActive("ahk_class WorkerW ahk_exe explorer.exe") && !WinActive("ahk_class Progman ahk_exe explorer.exe")) {
; current active window matches one in the list
try {
if (WinGetMinMax("ahk_id " winHwnd) == 0) {
; window is not maximized or minimized
if (value.Rect.UpdatePosition(winHwnd) || !value.Rect.IsShowing) {
; window is in a new position or the gui was previously hidden
value.Rect.Show()
}
continue
}
}
; active window has been maximized, minimized, or no longer exists, so hide the rect
value.Rect.Hide()
continue
}
; hide any existing rects for non-matching windows
if (value.Rect.IsShowing) {
value.Rect.Hide()
continue
}
}
}
SetWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwFlags) {
return DllCall("SetWinEventHook"
, "UInt", eventMin ; start of event range
, "UInt", eventMax ; end of event range
, "UInt", hmodWinEventProc ; handle to DLL that contains the hook function or null
, "UInt", lpfnWinEventProc ; the callback
, "UInt", idProcess ; process IDs of interest (0 = all)
, "UInt", idThread ; thread IDs of interest (0 = all)
, "UInt", dwFlags ; flags
, "Ptr") ; returns HWINEVENTHOOK
}
ReleaseResources(ExitReason, ExitCode) {
global hookProcAdr, hWinEventHook
loop hWinEventHooks.Length {
DllCall("UnhookWinEvent", "Ptr", hWinEventHooks.Pop()) ; free up memory from SetWinEventHook
}
if (hookProcAdr) {
CallbackFree(hookProcAdr) ; free up allocated memory from CallbackCreate
hookProcAdr := ""
}
}
OnExit(ReleaseResources)
hookProcAdr := CallbackCreate(ShowRect, , 7) ; avoiding fast mode because it may be called from a different thread
; hook on active window change
hWinEventHooks.Push(SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, 0, hookProcAdr, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS))
; hook on window move/resize
hWinEventHooks.Push(SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE, 0, hookProcAdr, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS))
ShowRect(0, EVENT_SYSTEM_FOREGROUND, WinActive("A")) ; Make a pseudo call when the script is called in order to affect the current active window as well
; =================================================================
m:: MsgBox("m")
Here is one way to do what @PGilm was suggesting. It's not the most eloquent solution, but it should work for your needs.
You could put something like this inside the Call()
method of DrawRect
, after these first four lines
if (win := this.func()) && WinGetMinMax(win) == 0 {
WinGetPos(&x, &y, &w, &h, win)
offset := this.offset
border_thickness := this.border_thickness
add
title := WinGetTitle("ahk_id " win)
if (title ~= "- Notepad$") {
border_thickness := 4
offset := 1
super.BackColor := "green"
} else if (title == "Microsoft Store") {
border_thickness := 2
offset := 3
super.BackColor := "red"
} else {
super.BackColor := this.border_color
}
additionally, you'll need to change the line in the __New()
method from
super.BackColor := border_color
to
this.border_color := border_color
in order to store the default border_color
.
What's happening is that SetTimer
is being passed a function object and its Call()
method will be called every 100 milliseconds.
If I were writing it from scratch, to make it more versatile, I would probably have separate objects set up and then call the right one depending on the current active window.
An alternative way of doing this is to watch for the EVENT_SYSTEM_FOREGROUND
and EVENT_OBJECT_LOCATIONCHANGE
messages being sent by the system via SetWinEventHook
and then show/hide the rect's appropriately.
By doing is this way, you won't have a bunch of SetTimer
s running in the background eating up cpu or memory. However, the rects also won't go away after a predetermined period of time this way. EVENT_OBJECT_LOCATIONCHANGE
should account for this in most situations, but in the event that you want to watch for additional system events, I'll include a list of a number of them at the top and also a link to view more of them.
Persistent ; Keep the script running even when there are no hotkeys or timers
; used in SetWinEventHook (for eventMin and eventMax)
; check here for additional events: https://learn.microsoft.com/en-us/windows/win32/winauto/event-constants
global EVENT_SYSTEM_ALERT := 0x2 ; 2 - alert has been generated
global EVENT_SYSTEM_FOREGROUND := 0x3 ; 3 - foreground window changed
global EVENT_SYSTEM_MENUSTART := 0x4 ; 4 - menu-bar menu opened
global EVENT_SYSTEM_MENUEND := 0x5 ; 5 - menu-bar menu closed
global EVENT_SYSTEM_MENUPOPUPSTART := 0x6 ; 6 - pop-up menu opened
global EVENT_SYSTEM_MENUPOPUPEND := 0x7 ; 7 - pop-up menu closed
global EVENT_SYSTEM_CAPTURESTART := 0x8 ; 8 - mouse click start
global EVENT_SYSTEM_CAPTUREEND := 0x9 ; 9 - mouse click end
global EVENT_SYSTEM_MOVESIZESTART := 0xA ; 10 - window move/resize start
global EVENT_SYSTEM_MOVESIZEEND := 0xB ; 11 - window move/resize end
global EVENT_SYSTEM_SCROLLINGSTART := 0x12 ; 18 - scrolling started on a scrol bar
global EVENT_SYSTEM_SCROLLINGEND := 0x13 ; 19 - scrolling ended on a scrol bar
global EVENT_SYSTEM_SWITCHSTART := 0x14 ; 20 - alt+tab pressed
global EVENT_SYSTEM_SWITCHEND := 0x15 ; 21 - alt+tab released
global EVENT_SYSTEM_MINIMIZESTART := 0x16 ; 22 - window minimized
global EVENT_SYSTEM_MINIMIZEEND := 0x17 ; 23 - window restored from minimize
global WINDOW_DRAG_STARTED := 0x19 ; 25 - window drag start (documentation not found for this)
global EVENT_SYSTEM_DESKTOPSWITCH := 0x20 ; 32 - active desktop has switched
global EVENT_OBJECT_CREATE := 0x8000 ; 32768 - an object has been created
global EVENT_OBJECT_DESTROY := 0x8001 ; 32769 - an object has been destroyed
global EVENT_OBJECT_SHOW := 0x8002 ; 32770 - a hidden object is shown
global EVENT_OBJECT_LOCATIONCHANGE := 0x800B ; 32779 - an object has changed location, shape, or size
global EVENT_OBJECT_NAMECHANGE := 0x800C ; 32780 - an object's Name property has changed
global EVENT_OBJECT_VALUECHANGE := 0x800E ; 32782 - an object's Value property has changed
global EVENT_OBJECT_PARENTCHANGE := 0x800F ; 32783 - an object has a new parent object
global EVENT_OBJECT_CLOAKED := 0x8017 ; 32791 - a window is cloaked. A cloaked window still exists, but is invisible to the user.
global EVENT_OBJECT_UNCLOAKED := 0x8018 ; 32792 - a window is uncloaked. A cloaked window still exists, but is invisible to the user.
; used in SetWinEventHook (for dwFlags)
global WINEVENT_OUTOFCONTEXT := 0x0000 ; Events are ASYNC
global WINEVENT_SKIPOWNTHREAD := 0x0001 ; Don't call back for events generated by this thread
global WINEVENT_SKIPOWNPROCESS := 0x0002 ; Don't call back for events generated by threads of this process
; used with the callback function of SetWinEventHook (for idObject and idChild)
global OBJID_WINDOW := 0x00000000 ; 0
global OBJID_SYSMENU := 0xFFFFFFFF ; -1
global OBJID_TITLEBAR := 0xFFFFFFFE ; -2
global OBJID_MENU := 0xFFFFFFFD ; -3
global OBJID_CLIENT := 0xFFFFFFFC ; -4
global OBJID_VSCROLL := 0xFFFFFFFB ; -5
global OBJID_HSCROLL := 0xFFFFFFFA ; -6
global OBJID_SIZEGRIP := 0xFFFFFFF9 ; -7
global OBJID_CARET := 0xFFFFFFF8 ; -8
global OBJID_CURSOR := 0xFFFFFFF7 ; -9
global OBJID_ALERT := 0xFFFFFFF6 ; -10
global OBJID_SOUND := 0xFFFFFFF5 ; -11
global OBJID_QUERYCLASSNAMEIDX := 0xFFFFFFF4 ; -12
global OBJID_NATIVEOM := 0xFFFFFFF3 ; -13
global CHILDID_SELF := 0
; saved for releasing resources appropriately
global hookProcAdr := ""
global hWinEventHooks := []
; list of windows for which to create a border
; Title is what will be matched with WinActive(Title)
; if the final list entry's Title is "A", it will make the current active window be the "fallback" if none of the others match
global rectList := [
{Title: "ahk_class PotPlayer64", Rect: DrawRect(2, 'green', 0)},
{Title: "ahk_exe vivaldi.exe", Rect: DrawRect(2, 'yellow', 2)},
{Title: "ahk_exe Reverso.exe", Rect: DrawRect(2, 'red', 0)},
{Title: "ahk_group desktop", Rect: DrawRect(2, '33cc33', 2)},
{Title: "- Notepad", Rect: DrawRect(4, 'green', 1)},
{Title: "Microsoft Store", Rect: DrawRect(2, 'red', 3)},
{Title: "A", Rect: DrawRect()}
]
class DrawRect extends Gui {
__New(border_thickness := "3", border_color := "red", offset := 0) {
super.__New("+AlwaysOnTop -Caption +ToolWindow", "GUI4Boarder")
super.BackColor := border_color
super.Opt("+E0x08080028") ; WS_EX_NOACTIVATE|WS_EX_LAYERED|WS_EX_TRANSPARENT|WS_EX_TOPMOST
DllCall("SetLayeredWindowAttributes", "Ptr",super.Hwnd, "Ptr",0, "UChar",255, "UInt",2, "Int") ; allows this "Layered Window" to be visible and makes it fully opaque (i.e. 255)
this.offset := offset
this.parentHwnd := 0
this.x := 0
this.y := 0
this.w := 0
this.h := 0
this.outerX := offset
this.outerY := offset
this.innerX := border_thickness + offset
this.innerY := border_thickness + offset
this.IsShowing := false
}
UpdatePosition(hwnd) {
this.parentHwnd := hwnd
super.Opt("+Owner" this.parentHwnd)
WinGetPos(&x, &y, &w, &h, this.parentHwnd)
if (x == this.x && y == this.y && w == this.w && h == this.h)
return false ; position is the same as the last time this was checked
this.x := x
this.y := y
this.w := w
this.h := h
return true
}
Show() {
outerX2 := this.w - this.outerX
outerY2 := this.h - this.outerY
innerX2 := this.w - this.innerX
innerY2 := this.h - this.innerY
WinSetRegion(this.outerX "-" this.outerY " " outerX2 "-" this.outerY " " outerX2 "-" outerY2 " " this.outerX "-" outerY2 " " this.outerX "-" this.outerY " " this.innerX "-" this.innerY " " innerX2 "-" this.innerY " " innerX2 "-" innerY2 " " this.innerX "-" innerY2 " " this.innerX "-" this.innerY, super.Hwnd)
; super.Show("w" . this.w . " h" . this.h . " x" . this.x . " y" . this.y . " NoActivate")
; Similar to super.Show(whxy) but also places it at the top of the z-order
DllCall("SetWindowPos", "Ptr",super.Hwnd, "Ptr",0, "Int",this.x, "Int",this.y, "Int",this.w, "Int",this.h, "UInt",0x4250, "Int") ; SWP_NOACTIVATE|SWP_SHOWWINDOW|SWP_NOOWNERZORDER|SWP_ASYNCWINDOWPOS (0x10|0x40|0x200|0x4000)
this.IsShowing := true
}
Hide() {
super.Hide()
this.IsShowing := false
}
}
ShowRect(hWinEventHook, Event, hWnd, idObject:=0, idChild:=0, dwEventThread:=0, dwmsEventTime:=0) {
Critical -1 ; turns on Critical (making the thread uninterruptible) but disables message checks (via -1)
; ignore events for objects that are not windows and events for child objects
if (idObject !== OBJID_WINDOW || idChild !== CHILDID_SELF)
return
for _,value in rectList {
; the latter two WinActive checks will exclude the Windows Desktop itself from matching but you can remove them if you aren't using a WinActive("A") gui
if ((winHwnd := WinActive(value.Title)) && !WinActive("ahk_class WorkerW ahk_exe explorer.exe") && !WinActive("ahk_class Progman ahk_exe explorer.exe")) {
; current active window matches one in the list
try {
if (WinGetMinMax("ahk_id " winHwnd) == 0) {
; window is not maximized or minimized
if (value.Rect.UpdatePosition(winHwnd) || !value.Rect.IsShowing) {
; window is in a new position or the gui was previously hidden
value.Rect.Show()
}
continue
}
}
; active window has been maximized, minimized, or no longer exists, so hide the rect
value.Rect.Hide()
continue
}
; hide any existing rects for non-matching windows
if (value.Rect.IsShowing) {
value.Rect.Hide()
continue
}
}
}
SetWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwFlags) {
return DllCall("SetWinEventHook"
,"UInt",eventMin ; start of event range
,"UInt",eventMax ; end of event range
,"UInt",hmodWinEventProc ; handle to DLL that contains the hook function or null
,"UInt",lpfnWinEventProc ; the callback
,"UInt",idProcess ; process IDs of interest (0 = all)
,"UInt",idThread ; thread IDs of interest (0 = all)
,"UInt",dwFlags ; flags
,"Ptr") ; returns HWINEVENTHOOK
}
ReleaseResources(ExitReason, ExitCode) {
global hookProcAdr, hWinEventHook
loop hWinEventHooks.Length {
DllCall("UnhookWinEvent", "Ptr",hWinEventHooks.Pop()) ; free up memory from SetWinEventHook
}
if (hookProcAdr) {
CallbackFree(hookProcAdr) ; free up allocated memory from CallbackCreate
hookProcAdr := ""
}
}
OnExit(ReleaseResources)
hookProcAdr := CallbackCreate(ShowRect, , 7) ; avoiding fast mode because it may be called from a different thread
; hook on active window change
hWinEventHooks.Push(SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, 0, hookProcAdr, 0, 0, WINEVENT_OUTOFCONTEXT|WINEVENT_SKIPOWNPROCESS))
; hook on window move/resize
hWinEventHooks.Push(SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE, 0, hookProcAdr, 0, 0, WINEVENT_OUTOFCONTEXT|WINEVENT_SKIPOWNPROCESS))
ShowRect(0, EVENT_SYSTEM_FOREGROUND, WinActive("A")) ; Make a pseudo call when the script is called in order to affect the current active window as well
I also moved some of the expressions to the __New()
method for a minimal optimization.
Added an UpdatePosition(hwnd)
method that returns false if the position is unchanged and modified the Show()
method and callsites of it accordingly.
Here is a version that I was playing around with until I settled on the EVENT_OBJECT_LOCATIONCHANGE
event. To be clear, this is an "incomplete" solution compared to Version 2, but it may assist you in debugging things yourself.
Just replace the ShowRect
method in Version 2 with the one below and modify the additional line below that. There are some superfluous switch cases accounted for, since the only events that will show up are the ones that you include in the SetWinHookEvent
. However, the main problem with this version is that it won't account for situations like where you maximize the window.
ShowRect(hWinEventHook, Event, hWnd, idObject:=0, idChild:=0, dwEventThread:=0, dwmsEventTime:=0) {
; ignore events for objects that are not windows and events for child objects
if (idObject !== OBJID_WINDOW || idChild !== CHILDID_SELF)
return
switch (Event) {
case EVENT_SYSTEM_SCROLLINGSTART
, EVENT_SYSTEM_SCROLLINGEND
, EVENT_OBJECT_CREATE
, EVENT_OBJECT_DESTROY
, EVENT_OBJECT_SHOW
, EVENT_OBJECT_NAMECHANGE
, EVENT_OBJECT_VALUECHANGE
, EVENT_OBJECT_PARENTCHANGE:
; do nothing
return
case EVENT_SYSTEM_FOREGROUND
, EVENT_SYSTEM_MOVESIZESTART
, EVENT_SYSTEM_MOVESIZEEND
, EVENT_SYSTEM_MENUSTART
, EVENT_SYSTEM_MENUEND
, EVENT_SYSTEM_MENUPOPUPSTART
, EVENT_SYSTEM_MENUPOPUPEND
, EVENT_SYSTEM_CAPTURESTART
, EVENT_SYSTEM_CAPTUREEND
, EVENT_SYSTEM_SWITCHEND
, EVENT_SYSTEM_MINIMIZEEND
, WINDOW_DRAG_STARTED
, EVENT_OBJECT_UNCLOAKED:
for _,value in rectList {
; the latter two WinActive checks will exclude the Windows Desktop itself from matching but you can remove them if you aren't using a WinActive("A") gui
if ((winHwnd := WinActive(value.Title)) && !WinActive("ahk_class WorkerW ahk_exe explorer.exe") && !WinActive("ahk_class Progman ahk_exe explorer.exe")) {
; current active window matches one in the list
try {
if (WinGetMinMax("ahk_id " winHwnd) == 0) {
; window is not maximized or minimized
if (value.Rect.UpdatePosition(winHwnd) || !value.Rect.IsShowing) {
; window is in a new position or the gui was previously hidden
value.Rect.Show()
}
continue
}
}
; active window has been maximized, minimized, or no longer exists, so hide the rect
value.Rect.Hide()
continue
}
; hide any existing rects for non-matching windows
if (value.Rect.IsShowing) {
value.Rect.Hide()
continue
}
}
case EVENT_SYSTEM_ALERT
, EVENT_SYSTEM_MINIMIZESTART
, EVENT_SYSTEM_SWITCHSTART
, EVENT_SYSTEM_DESKTOPSWITCH
, EVENT_OBJECT_CLOAKED:
; hide any rects
for _,value in rectList {
if (value.Rect.IsShowing) {
value.Rect.Hide()
break
}
}
default:
; hide any rects
for _,value in rectList {
if (value.Rect.IsShowing) {
value.Rect.Hide()
break
}
}
return
}
}
Then you can change the hooks to only include this one, which will hook on every event from EVENT_SYSTEM_FOREGROUND
through EVENT_SYSTEM_DESKTOPSWITCH
:
hWinEventHooks.Push(SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_DESKTOPSWITCH, 0, hookProcAdr, 0, 0, WINEVENT_OUTOFCONTEXT|WINEVENT_SKIPOWNPROCESS))
Or, alternatively, include only the exact ones you want to account for, similar to Version 2.