Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- This script simulates native non-fullscreen taskbar functionality in specific fullscreen windows. 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). On top of that, it is very lightweight due to the mouse hook it uses.
- Tested with Windows 10 Pro 64-bit version 19045.4170 (22H2)
- */
- #Requires AutoHotkey 2.1-alpha.9
- #NoTrayIcon
- Persistent()
- A_CoordModePixel := "Screen"
- A_KeyDelay := -1 ; For Send b/c it reverts to the Event SendMode when another AHK script installs a low-level keyboard hook
- A_WinDelay := -1
- ;GroupAdd("ChromeAndExclusion", "ahk_exe chrome.exe",, "YouTube")
- ;GroupAdd("ChromeAndTwoExclusions", "ahk_group ChromeAndExclusion",, "Steam")
- ;GroupAdd("ChromeAndThreeExclusions", "ahk_group ChromeAndTwoExclusions",, "IMDb")
- affectedWindows :=
- [
- ; Use an array with only the parameters that you need:
- ; ["WinTitle", "WinText", "ExcludeTitle", "ExcludeText"],
- ["Sourcetree ahk_exe SourceTree.exe"],
- ;["ahk_group ChromeAndThreeExclusions"] ; In-cognito needs to get excluded completely for this to be useful^^
- ]
- Initialize()
- #HotIf (WinActive("ahk_group AffectedWindows"))
- ~LButton::
- ~F11::
- ManageUnhiderState(*)
- {
- static fullscreenState := 2
- Sleep(100)
- DllCall("Shell32\SHQueryUserNotificationState", "UInt*", &monitorState := 0)
- if (SharedMouseHook.IsEnabled)
- {
- ; The helper-GUI might've become active after the delay if the cursor clicked something near the taskbar, in that case it shouldn't deactivate the unhider
- if (WinActive("ahk_group AffectedWindows") && monitorState != fullscreenState)
- DeactivateUnhider()
- }
- else if (monitorState = fullscreenState)
- {
- SharedMouseHook.IsEnabled := true
- HelperGui.SetProperties("DefaultsOpposite", "Transparency")
- HotIf()
- Hotkey("F1", (*) => Hotkey("F1", "Off"), "On")
- Send("{F1}") ; Necessary to make the taskbar immediately responsive (modifier key[s] being pressed by the user are ignored, even when SendMode reverts to Event)
- SharedMouseHook.Instance.Start()
- }
- }
- #HotIf (WinActive(A_ScriptName " ahk_class AutoHotkeyGUI") && SharedMouseHook.IsEnabled)
- $F11::HelperGui.SetProperties("Defaults"), WinActivate(SharedMouseHook.AffectedContextWnd), Send("{F11}")
- $!Esc::HelperGui.SetProperties("Defaults"), WinActivate(SharedMouseHook.AffectedContextWnd), Send("!{Esc}")
- ;********** LIBRARY **********
- Initialize()
- {
- HelperGui() ; Has to be manually initialized early, so that it's rdy when any of its members get used
- for (iteratedAffectedWnd in affectedWindows)
- GroupAdd("AffectedWindows", iteratedAffectedWnd*)
- EVENT_SYSTEM_FOREGROUND := 0x0003
- DllCall("SetWinEventHook", "UInt", EVENT_SYSTEM_FOREGROUND, "UInt", EVENT_SYSTEM_FOREGROUND, "Ptr", 0, "Ptr", CallbackCreate(HandleAffectedWinEvent), "UInt", 0, "UInt", 0, "UInt", 0)
- HandleAffectedWinEvent(hWinEventHook, event, hWnd, *)
- {
- if (WinActive("ahk_group AffectedWindows"))
- ManageUnhiderState()
- else if (!(WinActive(HelperGui.Id) && DetermineIsAffectedWindowBehindActive()) && SharedMouseHook.IsEnabled)
- DeactivateUnhider()
- DetermineIsAffectedWindowBehindActive()
- {
- WS_CAPTION := 0xC00000 ; title bar
- for (iteratedId in WinGetList())
- if (WinActive(iteratedId) && WinGetClass(iteratedId) != "Progman")
- continue
- else if (DetermineIsAffectedWnd(iteratedId))
- return true
- else if (WinGetClass(iteratedId) = "Progman" || WinGetMinMax(iteratedId) != -1 && WinGetStyle(iteratedId) & WS_CAPTION && WinGetTitle(iteratedId) != "")
- return false
- DetermineIsAffectedWnd(winId)
- {
- for (iteratedAffectedWnd in affectedWindows)
- if (WinExist(iteratedAffectedWnd*) = winId)
- return true
- return false
- }
- }
- }
- }
- DeactivateUnhider()
- {
- SharedMouseHook.IsEnabled := false
- HelperGui.SetProperties("Defaults", "Transparency")
- SharedMouseHook.Instance.Stop()
- }
- class HelperGui
- {
- static __New()
- {
- ; Preparation to remove helper-GUI icon from taskbar and Alt+Tab window
- 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
- this.Id := -1
- EVENT_OBJECT_CREATE := 0x8000
- DllCall("SetWinEventHook", "UInt", EVENT_OBJECT_CREATE, "UInt", EVENT_OBJECT_CREATE, "Ptr", 0, "Ptr", CallbackCreate(HandleHelperGuiEvent), "UInt", 0, "UInt", 0, "UInt", 0)
- loop (10)
- {
- try ComCall(3, tbl) ; Calls tbl.HrInit(), which initializes the TaskbarList object
- catch
- Sleep(50)
- else
- break
- }
- HandleHelperGuiEvent(hWinEventHook, event, hWnd, *)
- {
- if (hWnd = this.Id)
- {
- ComCall(5, tbl, "Ptr", hWnd) ; Calls tbl.DeleteTab(winId), which deletes a tab from the TaskbarList
- DllCall("UnhookWinEvent", "Ptr", hWinEventHook)
- }
- }
- ; Creating a helper-GUI, which is used for two things:
- ; Always-on-top deactivated taskbar line makes it seem as if doesn't disappear in affected window & not always-on-top but active to unhide the taskbar when necessary
- this._Gui := Gui("-DPIScale")
- this._Gui.BackColor := PixelGetColor(A_ScreenWidth - 10, A_ScreenHeight - 1) ; The color is retrieved from a taskbar area of which its color never changes
- this.Id := WinExist(this._Gui)
- this.SetProperties("Defaults", "Transparency") ; Initially invisible (it gets made visible when necessary) and always-on-top (it exits this state when necessary)
- WinSetStyle("-0xC00000", this._Gui) ; Removing the title bar
- this._Gui.Show("w" A_ScreenWidth " h2 y" A_ScreenHeight - 2) ; This will make it pretend to be the deactivated taskbar line (b/c the taskbar is hiding completely in fullscreen)
- ComCall(5, tbl, "Ptr", this.Id) ; Calls tbl.DeleteTab(winId), which deletes a tab from the TaskbarList (is extra, in case if HandleHelperGuiEvent didn't succeed doing it early)
- }
- static SetProperties(setMode, transparencyMode := "SkipTransparency")
- {
- if (setMode = "Defaults")
- {
- this.HasActivated := false
- WinSetAlwaysOnTop(true, this._Gui)
- if (transparencyMode = "Transparency")
- WinSetTransparent(0, this._Gui)
- }
- else if (setMode = "DefaultsOpposite")
- {
- if (transparencyMode = "Transparency")
- WinSetTransparent("Off", this._Gui)
- else
- {
- this.HasActivated := true
- WinSetAlwaysOnTop(false, this._Gui)
- }
- }
- }
- static MoveToRelevantMonitor()
- {
- if (MonitorGetCount() > 1)
- this._Gui.Move(Monitor.ContextInfo.RcMonitor.LeftX, Taskbar.DeactivatedContextY, Taskbar.ContextWidth, 2)
- }
- }
- class Cursor
- {
- static IsOnRelevantDeactivatedTaskbarLine
- {
- get
- {
- Monitor()
- return this.Y >= Taskbar.DeactivatedContextY && this.DetermineIsOnActiveWindowMonitor()
- }
- }
- static IsOffRelevantDeactivatedTaskbarFalseOnError
- {
- get
- {
- return DetermineIsOffRelevantTaskbarFalseOnError(¤tTaskbarY) && this.DetermineIsOnActiveWindowMonitor() && currentTaskbarY = Taskbar.DeactivatedContextY
- DetermineIsOffRelevantTaskbarFalseOnError(¤tTaskbarY)
- {
- Monitor()
- try WinGetPos(, ¤tTaskbarY,,, Monitor.ContextInfo.DwFlags = this.MONITORINFOF_PRIMARY ? "ahk_class Shell_TrayWnd" : "ahk_class Shell_SecondaryTrayWnd")
- catch
- return false
- return this.Y < currentTaskbarY
- }
- }
- }
- static __New() => this.MONITORINFOF_PRIMARY := 1
- __New(x, y) => (Cursor.X := x, Cursor.Y := y)
- static DetermineIsOnActiveWindowMonitor() => this.X >= Monitor.ContextInfo.RcMonitor.LeftX && this.X <= Monitor.ContextInfo.RcMonitor.RightX
- }
- class Monitor
- {
- __New() => DllCall("GetMonitorInfo", "Ptr", DllCall("MonitorFromWindow", "UInt", WinExist("A"), "UInt", MONITOR_DEFAULTTOPRIMARY := 1), "Ptr", Monitor.ContextInfo := MonitorInfo())
- }
- class Taskbar
- {
- static DeactivatedContextY => Monitor.ContextInfo.RcMonitor.BottomY - 2
- static ContextWidth => Monitor.ContextInfo.RcMonitor.RightX - Monitor.ContextInfo.RcMonitor.LeftX
- static __New() => this.ContextClass := 0
- }
- class Rect
- {
- LeftX: i32, TopY: i32, RightX: i32, BottomY: i32
- }
- class MonitorInfo
- {
- CbSize: i32 := ObjGetDataSize(this), RcMonitor: Rect, RcWork: Rect, DwFlags: i32
- }
- class SharedMouseHook
- {
- static __New()
- {
- this.Instance := PointerDeviceHook("Move", HandleMouseMoveEvent(eventInfo)
- {
- Cursor(eventInfo.Pt.X, eventInfo.Pt.Y)
- if (!HelperGui.HasActivated && Cursor.IsOnRelevantDeactivatedTaskbarLine)
- {
- HelperGui.SetProperties("DefaultsOpposite")
- HelperGui.MoveToRelevantMonitor()
- SharedMouseHook.AffectedContextWnd := WinExist("A")
- WinActivate(HelperGui.Id)
- }
- else if (HelperGui.HasActivated && Cursor.IsOffRelevantDeactivatedTaskbarFalseOnError)
- {
- HelperGui.SetProperties("Defaults")
- WinActivate(SharedMouseHook.AffectedContextWnd)
- }
- }, true)
- this.IsEnabled := false
- this.AffectedContextWnd := 0
- }
- }
- class Point
- {
- X: i32, Y: i32
- }
- /*
- Example usage: (hook := PointerDeviceHook("Move|LButton Down|LButton Up", HandlePointerDeviceEvent)).Start()
- /** @param {PointerDeviceHook.EventInfo} eventInfo Relevant 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 => PointerDeviceHook.EventInfo.IsKeyPressedDown && this._IsPtAdaptive ? PointerDeviceHook.EventInfo.LatestPt : this._Pt
- /** @prop {string} Action The event action */
- Action => PointerDeviceHook.EventInfo.IsKeyPressedDown ? (PointerDeviceHook.EventInfo.LatestAction ?? this._Action) : this._Action
- /** @prop {string} Key The event key */
- Key => PointerDeviceHook.EventInfo.IsKeyPressedDown ? PointerDeviceHook.EventInfo.KeyToKeep : this._Key
- /** @prop {integer} KeyUsageCount Count how many times a key gets pressed in a short time (`KeyUsageCountTimeThreshold`) */
- KeyUsageCount => PointerDeviceHook.EventInfo.IsKeyPressedDown ? PointerDeviceHook.EventInfo.KeyUsageCountToKeep : 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.KeyUsageTimeToKeep : 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.IsPenActionToKeep : 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 actions and message codes */
- MsgList := Map(
- 512, "Move",
- 513, "LButton Down",
- 514, "LButton Up",
- 516, "RButton Down",
- 517, "RButton Up",
- 519, "MButton Down",
- 520, "MButton Up",
- 522, "Wheel",
- 523, "XButton{:d} Down",
- 524, "XButton{:d} Up")
- /** @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_LBUTTONUP := 0x0202, WM_RBUTTONUP := 0x0205, 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()
- eventInfo._IsPtAdaptive := false
- eventInfo._Key := 0
- switch (wParam)
- {
- ; 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)
- case WM_MOUSEWHEEL: eventInfo._Action := (internalEventInfo.MouseData >> 16) > 0 ? "WheelUp" : "WheelDown"
- case WM_XBUTTONDOWN, WM_XBUTTONUP: eventInfo._Action := Format(this.MsgList[wParam], internalEventInfo.MouseData >> 16)
- 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)
- if (eventInfo._Action && eventInfo._Action != "Move")
- {
- isKeyUpEvent := false
- switch (wParam)
- {
- case WM_LBUTTONUP, WM_RBUTTONUP, WM_MBUTTONUP, WM_XBUTTONUP: isKeyUpEvent := true
- }
- if (eventInfo._Key != eventInfo._Action)
- eventInfo._Key := eventInfo._Action
- if (!this.PriorKeyObjects.Has(eventInfo._Action))
- this.PriorKeyObjects[eventInfo._Action] := {Time: internalEventInfo.Time, Count: 0}
- priorKey := this.PriorKeyObjects[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 (isKeyUpEvent || wParam = WM_MOUSEWHEEL)
- {
- eventInfo._Pt.X := internalEventInfo.Pt.X
- eventInfo._Pt.Y := internalEventInfo.Pt.Y
- PointerDeviceHook.EventInfo.IsKeyPressedDown := false
- }
- else
- {
- eventInfo._IsPtAdaptive := true
- PointerDeviceHook.EventInfo.LatestPt.X := internalEventInfo.Pt.X
- PointerDeviceHook.EventInfo.LatestPt.Y := internalEventInfo.Pt.Y
- PointerDeviceHook.EventInfo.LatestAction := unset
- PointerDeviceHook.EventInfo.KeyToKeep := eventInfo._Key
- PointerDeviceHook.EventInfo.KeyUsageCountToKeep := eventInfo._KeyUsageCount
- PointerDeviceHook.EventInfo.KeyUsageTimeToKeep := internalEventInfo.Time
- PointerDeviceHook.EventInfo.IsPenActionToKeep := eventInfo._IsPenAction
- PointerDeviceHook.EventInfo.IsKeyPressedDown := true
- SetTimer(() => (this.ProcHandle && PointerDeviceHook.EventInfo.IsKeyPressedDown ? onEvent(eventInfo) : SetTimer(, 0)), 50)
- }
- }
- else if (PointerDeviceHook.EventInfo.IsKeyPressedDown)
- {
- PointerDeviceHook.EventInfo.LatestPt.X := eventInfo._Pt.X := internalEventInfo.Pt.X
- PointerDeviceHook.EventInfo.LatestPt.Y := eventInfo._Pt.Y := internalEventInfo.Pt.Y
- PointerDeviceHook.EventInfo.LatestAction := eventInfo._Action
- }
- else
- {
- eventInfo._Pt.X := internalEventInfo.Pt.X
- eventInfo._Pt.Y := internalEventInfo.Pt.Y
- eventInfo._KeyUsageTime := eventInfo._KeyUsageCount := eventInfo._Key := 0
- 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))
- }
Add Comment
Please, Sign In to add comment