Skip to content

Instantly share code, notes, and snippets.

@zero-plusplus
Last active May 14, 2022 14:51
Show Gist options
  • Save zero-plusplus/88b4407e3a939233315a1e2e2fffd464 to your computer and use it in GitHub Desktop.
Save zero-plusplus/88b4407e3a939233315a1e2e2fffd464 to your computer and use it in GitHub Desktop.
This class provides methods to register/unregister window change event.
/**
* @author zero-plusplus (https://github.com/zero-plusplus)
* @licence MIT
* @link https://gist.github.com/zero-plusplus/88b4407e3a939233315a1e2e2fffd464
*/
/**
* Provides a way to register or unregister window change event.
* @example
* ; Always change to pseudo full screen when notepad is active
* WindowChangeEvent.register(Func("eventCallback"))
*
* eventCallback(windowInfo) {
* if (windowInfo.exe == "notepad.exe") {
* WinMove, A, , 0, 0, %A_ScreenWidth%, %A_ScreenHeight%
* }
* }
* @example
* ; If you are debugging with SciTE4AutoHotkey or Visual Studio Code, you should be careful that the editor freezes when the window change event is called by [WinActivate](https://www.autohotkey.com/docs/commands/WinActivate.htm).
* ; To avoid this, you must temporarily unregister before WinActivate.
*
* event := WindowChangeEvent.register(Func("eventCallback"))
* event.unregister()
* WinActivate, ahk_exe notepad.exe
* event.register()
*
* eventCallback(windowInfo) {
* }
*/
class WindowChangeEvent {
/**
* `true` if a callback is registered for the window change event, `false` otherwise.
* @type {boolean}
*/
isRegistered[] {
get {
return 0 < this.id
}
}
/**
* @param {(windowInfo: WindowChangeEvent.ActiveWindowInfo) => void} callback - Callable object called on window change.
*/
__NEW(callback := "") {
this.id := 0
if (!IsObject(callback)) {
throw Exception("``callback`` must be a Func, BoundFunc, or user-defined function.")
}
this.callback := callback
}
/**
* Registers a callback to the window change event.
* @static
* @param {(windowInfo: WindowChangeEvent.ActiveWindowInfo) => void} callback - Callable object called on window change.
* @return {WindowChangeEvent}
*/
/**
* Registers a pre-specified callback to the window change event.
* @chainable
*/
register(callback := "") {
; https://docs.microsoft.com/en-us/windows/win32/winauto/event-constants
static EVENT_SYSTEM_FOREGROUND := 0x00000003
; static method
if (this == WindowChangeEvent) {
return new WindowChangeEvent(callback).register()
}
; instance method
; https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwineventhook
this.id := DllCall( "SetWinEventHook"
, "UInt", eventMin := EVENT_SYSTEM_FOREGROUND
, "UInt", eventMax := EVENT_SYSTEM_FOREGROUND
, "UInt", hmodWinEventProc := 0
, "UInt", pfnWinEventProc := RegisterCallback(Func("WindowChangeEvent._callbackWrapper"), "", , &(this.callback))
, "UInt", idProcess := 0
, "UInt", idThread := 0
, "UInt", dwFlags := 0 )
return this
}
/**
* Unregisters a registered callback.
* @chainable
*/
unregister() {
if (DllCall("UnhookWinEvent", "UInt", this.id)) {
this.id := 0
}
return this
}
/**
* @private
* @static
*/
_callbackWrapper() {
callback := Object(A_EventInfo)
%callback%(WindowChangeEvent.ActiveWindowInfo)
}
/**
* Provides properties to get information about the active window.
*/
class ActiveWindowInfo {
/**
* Returns the title of the active window.
* @static
* @type {string}
*/
title {
get {
WinGetTitle, title, A
return title
}
}
/**
* Returns the window class of the active window.
* @static
* @type {string}
*/
class {
get {
WinGetClass, winClass, A
return winClass
}
}
/**
* It is the same as the `class` property except that it is prefixed with `"ahk_class "`.
* @static
* @type {string}
*/
ahk_class {
get {
return "ahk_class " . this.class
}
}
/**
* Returns the window id (hwnd) of the active window.
* @static
* @type {string}
*/
id {
get {
WinGet, id, ID, A
return id
}
}
/**
* It is the same as the `id` property except that it is prefixed with `"ahk_id "`.
* @static
* @type {string}
*/
ahk_id {
get {
return "ahk_id " . this.id
}
}
/**
* Returns the process id of the active window.
* @static
* @type {string}
*/
pid {
get {
WinGet, pid, PID, A
return pid
}
}
/**
* It is the same as the `pid` property except that it is prefixed with `"ahk_pid "`.
* @static
* @type {string}
*/
ahk_pid {
get {
return "ahk_pid " . this.pid
}
}
/**
* Returns the process name of the active window.
* @static
* @type {string}
*/
exe {
get {
WinGet, processName, ProcessName, A
return processName
}
}
/**
* It is the same as the `exe` property except that it is prefixed with `"ahk_exe "`.
* @static
* @type {string}
*/
ahk_exe {
get {
return "ahk_exe " . this.exe
}
}
/**
* Returns the window text of the active window.
* @static
* @type {string}
*/
text {
get {
WinGetText, text, A
return text
}
}
}
}
/**
* @author zero-plusplus (https://github.com/zero-plusplus)
* @licence MIT
* @link https://gist.github.com/zero-plusplus/88b4407e3a939233315a1e2e2fffd464
*/
/**
* Provides a way to register or unregister window change event.
* @example
* ; Always change to pseudo full screen when notepad is active
* WindowChangeEvent.register(eventCallback)
*
* eventCallback(windowInfo) {
* if (windowInfo.exe == "notepad.exe") {
* WinMove(0, 0, A_ScreenWidth, A_ScreenHeight, "A")
* }
* }
* @example
* ; If you are debugging with SciTE4AutoHotkey or Visual Studio Code, you should be careful that the editor freezes when the window change event is called by [WinActivate](https://www.autohotkey.com/docs/commands/WinActivate.htm).
* ; To avoid this, you must temporarily unregister before WinActivate.
*
* event := WindowChangeEvent.register(eventCallback)
* event.unregister()
* WinActivate("ahk_exe notepad.exe")
* event.register()
*
* eventCallback(windowInfo) {
* }
*/
class WindowChangeEvent {
/**
* `true` if a callback is registered for the window change event, `false` otherwise.
* @type {boolean}
*/
isRegistered {
get {
return 0 < this.id
}
}
/**
* @param {(windowInfo: WindowChangeEvent.ActiveWindowInfo) => void} callback - Callable object called on window change.
*/
__NEW(callback := "") {
this.id := 0
if (!IsObject(callback)) {
throw Error("``callback`` must be a Func, BoundFunc, or user-defined function.")
}
this.callback := callback
}
/**
* Registers a callback to the window change event.
* @static
* @param {(windowInfo: WindowChangeEvent.ActiveWindowInfo) => void} callback - Callable object called on window change.
* @return {WindowChangeEvent}
*/
static register(callback) {
return WindowChangeEvent(callback).register()
}
/**
* Registers a pre-specified callback to the window change event.
* @chainable
*/
register() {
; https://docs.microsoft.com/en-us/windows/win32/winauto/event-constants
static EVENT_SYSTEM_FOREGROUND := 0x00000003
; https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwineventhook
this.id := DllCall(
"SetWinEventHook",
"UInt", eventMin := EVENT_SYSTEM_FOREGROUND,
"UInt", eventMax := EVENT_SYSTEM_FOREGROUND,
"UInt", hmodWinEventProc := 0,
"UInt", pfnWinEventProc := CallbackCreate(ObjBindMethod(WindowChangeEvent, "_callbackWrapper", this.callback)),
"UInt", idProcess := 0,
"UInt", idThread := 0,
"UInt", dwFlags := 0
)
return this
}
/**
* Unregisters a registered callback.
* @chainable
*/
unregister() {
if (DllCall("UnhookWinEvent", "UInt", this.id)) {
this.id := 0
}
return this
}
/**
* @private
* @static
*/
static _callbackWrapper(callback) {
callback(WindowChangeEvent.ActiveWindowInfo)
}
/**
* Provides properties to get information about the active window.
*/
class ActiveWindowInfo {
/**
* Returns the title of the active window.
* @static
* @type {string}
*/
static title {
get {
title := WinGetTitle("A")
return title
}
}
/**
* Returns the window class of the active window.
* @static
* @type {string}
*/
static class {
get {
winClass := WinGetClass("A")
return winClass
}
}
/**
* It is the same as the `class` property except that it is prefixed with `"ahk_class "`.
* @static
* @type {string}
*/
static ahk_class {
get {
return "ahk_class " . this.class
}
}
/**
* Returns the window id (hwnd) of the active window.
* @static
* @type {string}
*/
static id {
get {
id := WinGetId("A")
return id
}
}
/**
* It is the same as the `id` property except that it is prefixed with `"ahk_id "`.
* @static
* @type {string}
*/
static ahk_id {
get {
return "ahk_id " . this.id
}
}
/**
* Returns the process id of the active window.
* @static
* @type {string}
*/
static pid {
get {
pid := WinGetPid("A")
return pid
}
}
/**
* It is the same as the `pid` property except that it is prefixed with `"ahk_pid "`.
* @static
* @type {string}
*/
static ahk_pid {
get {
return "ahk_pid " . this.pid
}
}
/**
* Returns the process name of the active window.
* @static
* @type {string}
*/
static exe {
get {
processName := WinGetProcessName("A")
return processName
}
}
/**
* It is the same as the `exe` property except that it is prefixed with `"ahk_exe "`.
* @static
* @type {string}
*/
static ahk_exe {
get {
return "ahk_exe " . this.exe
}
}
/**
* Returns the window text of the active window.
* @static
* @type {string}
*/
static text {
get {
text := WinGetText("A")
return text
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment