This script was written in reply to a request on the Ask for Help forum. See the comments for a description of each option.
#Persistent hover_task_buttons = 1 ; hover over taskbar button to activate window hover_task_group = 1 ; hover to select (or exit) from a window-group menu ; (for Windows' "Group similar taskbar buttons" feature) hover_task_min_info = 0 ; only show info for minimized windows, don't activate hover_start_button = 1 ; hover over start button to open start menu hover_min_max = 1 ; minimize or maximize by hovering over the min/max buttons hover_any_window = 0 ; activate any window by hovering (except the taskbar) hover_no_buttons = 1 ; ignore hover if a mouse button is in the down state hover_delay = 500 ; hover delay, from when the mouse stops moving ; WORKS, BUT NOT PERFECTLY: (brings the window forward, then puts it back in place) hover_keep_zorder = 0 ; don't bring windows forward, only activate them ; ; DisableHover - since menus aren't usually truly "activated", if any ; window in the DisableHover group *EXISTS*, hover will be ignored. ; GroupAdd, DisableHover, ahk_class #32768 ; I'm not certain whether this class is used only for menus; ; if not, you may need to comment out this line. GroupAdd, DisableHover, ahk_class MozillaDropShadowWindowClass CoordMode, Mouse, Screen SetTimer, hovercheck, 10 return ; Don't consider it hovering if the mouse was clicked. ; However, clicking and then moving a small amount will still trigger hover. ~LButton:: ~RButton:: ~MButton:: MouseGetPos, lastx, lasty return hovercheck: MouseGetPos, x, y if (x != lastx or y != lasty) { SetTimer, hovering, % -hover_delay lastx := x lasty := y if (remove_ttip_on_move) { ToolTip remove_ttip_on_move := false } } return hovering: if (hover_no_buttons && (GetKeyState("LButton") or GetKeyState("RButton"))) return if (WinExist("ahk_group DisableHover")) return ; hover over taskbar button to activate window: if (hover_task_buttons) { ; hovering over taskbar button. if (GetMouseTaskButton(win)) { if (win) { if (hover_task_min_info) { WinGet, min_max, MinMax, ahk_id %win% if (min_max = -1) { WinGetTitle, ti, ahk_id %win% ToolTip, %ti% (minimized) remove_ttip_on_move := true return } } if (hover_keep_zorder) { JustActivate(win) return } WinActivate, ahk_id %win% } ; May be a group button ("Group similar taskbar buttons") else Click return } else if (hover_task_group && WinActive("ahk_class Shell_TrayWnd")) { ; Check if we are hovering over a toolbar window, ; possibly a list of grouped buttons/windows. MouseGetPos,,, win, ctl, 2 ctl_parent := DllCall("GetParent", "uint", ctl) ; get control parent WinGetClass, ctl, ahk_id %ctl% ; get control class WinGetClass, ctl_parent, ahk_id %ctl_parent% ; get parent class WinGetClass, win, ahk_id %win% ; get window class if (win="BaseBar" ; probably a button group menu || (win="Shell_TrayWnd" ; taskbar && ctl_parent="MSTaskSwWClass")) ; task buttons Click ; (The win="BaseBar" check excludes the system notification area.) return } } ; hover over start button to open start menu: if (hover_start_button && !WinActive("ahk_class DV2ControlHost")) ; Start Menu { MouseGetPos,,, win, ctl WinGetTitle, ti, ahk_id %win% WinGetClass, cl, ahk_id %win% if ((cl = "Button" && ti = "Start")) ; Vista / Windows 7 or (cl = "Shell_TrayWnd" && ctl = "Button1") { Click return } } ; hover over minimize, maximize or help buttons on titlebar: if (hover_min_max) { MouseGetPos, x, y, win, ctl, 2 SendMessage, 0x84,, (x & 0xFFFF) | (y & 0xFFFF) << 16,, % "ahk_id " (ctl ? ctl : win) if ErrorLevel in 8,9 ; min,max (titlebar) { Click return } } ; hover over any window to focus: if (hover_any_window) { ifWinExist, ahk_id %win% ahk_class Shell_TrayWnd return ; don't activate the taskbar MouseGetPos,,, win if (!WinActive("ahk_id " win)) { if (hover_keep_zorder) JustActivate(win) else WinActivate, ahk_id %win% } } return JustActivate(hwnd) { if (WinActive("ahk_id " hwnd)) return ; Get the window which hwnd is positioned after, so hwnd's position ; in the z-order can be restored after activation. hwnd_prev := GetPrevWindow(hwnd) ; DllCall("GetWindow","uint",hwnd,"uint",3) would be simpler, ; but doesn't work right since it usually gets an invisible window ; which moves when we activate hwnd. ; Repositioning a window in the z-order sometimes sets AlwaysOnTop. WinGet, OldExStyle, ExStyle, ahk_id %hwnd% ;WinActivate, ahk_id %hwnd% ; -- best to use SetWinDelay,-1 if using WinActivate. DllCall("SetForegroundWindow", "uint", hwnd) DllCall("SetWindowPos", "uint", hwnd, "uint", hwnd_prev , "int", 0, "int", 0, "int", 0, "int", 0 , "uint", 0x13) ; NOSIZE|NOMOVE|NOACTIVATE (0x1|0x2|0x10) ; Note NOACTIVATE above: if this is not specified, SetWindowPos activates ; the window, bringing it forward (effectively ignoring hwnd_prev...) ; Check if AlwaysOnTop status changed. WinGet, ExStyle, ExStyle, ahk_id %hwnd% if (OldExStyle ^ ExStyle) & 0x8 WinSet, AlwaysOnTop, Toggle } ; Like GetWindow(hwnd, GW_HWNDPREV), but ignores invisible windows. GetPrevWindow(hwnd) { global GetPrevWindow_RetVal static cb_EnumChildProc if (!cb_EnumChildProc) cb_EnumChildProc := RegisterCallback("GetPrevWindow_EnumChildProc","F") ; Set default in case enumeration fails. GetPrevWindow_RetVal := DllCall("GetWindow", "uint", hwnd, "uint", 3) ; Enumerate all siblings of hwnd. hwnd_parent := DllCall("GetParent", "uint", hwnd) DllCall("EnumChildWindows", "uint", hwnd_parent, "uint", cb_EnumChildProc, "uint", hwnd) ; Return the last visible window before hwnd. return GetPrevWindow_RetVal } GetPrevWindow_EnumChildProc(test_hwnd, hwnd) { global GetPrevWindow_RetVal ; Continue until hwnd is enumerated. if (test_hwnd = hwnd) return false ; Remember the last visible window before hwnd. if (DllCall("IsWindowVisible", "uint", test_hwnd)) GetPrevWindow_RetVal := test_hwnd return true } ; Gets the index+1 of the taskbar button which the mouse is hovering over. ; Returns an empty string if the mouse is not over the taskbar's task toolbar. ; ; Some code and inspiration from Sean's TaskButton.ahk GetMouseTaskButton(ByRef hwnd) { MouseGetPos, x, y, win, ctl, 2 ; Check if hovering over taskbar. WinGetClass, cl, ahk_id %win% if (cl != "Shell_TrayWnd") return ; Check if hovering over a Toolbar. WinGetClass, cl, ahk_id %ctl% if (cl = "MSTaskListWClass" ; Windows 7: the methods used below won't work. || A_PtrSize=8 ; Script not compatible with 64-bit AutoHotkey.exe. || DllCall("IsWow64Process", "Uint", DllCall("GetCurrentProcess") , "intP", iswow64) && iswow64) ; OS/taskbar is 64-bit - not compatible. { hwnd := 0 return 1 } if (cl != "ToolbarWindow32") return ; Check if hovering over task-switching buttons (specific toolbar). hParent := DllCall("GetParent", "Uint", ctl) WinGetClass, cl, ahk_id %hParent% if (cl != "MSTaskSwWClass") return WinGet, pidTaskbar, PID, ahk_class Shell_TrayWnd hProc := DllCall("OpenProcess", "Uint", 0x38, "int", 0, "Uint", pidTaskbar) pRB := DllCall("VirtualAllocEx", "Uint", hProc , "Uint", 0, "Uint", 20, "Uint", 0x1000, "Uint", 0x4) VarSetCapacity(pt, 8, 0) NumPut(x, pt, 0, "int") NumPut(y, pt, 4, "int") ; Convert screen coords to toolbar-client-area coords. DllCall("ScreenToClient", "uint", ctl, "uint", &pt) ; Write POINT into explorer.exe. DllCall("WriteProcessMemory", "uint", hProc, "uint", pRB+0, "uint", &pt, "uint", 8, "uint", 0) ; SendMessage, 0x447,,,, ahk_id %ctl% ; TB_GETHOTITEM SendMessage, 0x445, 0, pRB,, ahk_id %ctl% ; TB_HITTEST btn_index := ErrorLevel ; Convert btn_index to a signed int, since result may be -1 if no 'hot' item. if btn_index > 0x7FFFFFFF btn_index := -(~btn_index) - 1 if (btn_index > -1) { ; Get button info. SendMessage, 0x417, btn_index, pRB,, ahk_id %ctl% ; TB_GETBUTTON VarSetCapacity(btn, 20) DllCall("ReadProcessMemory", "Uint", hProc , "Uint", pRB, "Uint", &btn, "Uint", 20, "Uint", 0) state := NumGet(btn, 8, "UChar") ; fsState pdata := NumGet(btn, 12, "UInt") ; dwData ret := DllCall("ReadProcessMemory", "Uint", hProc , "Uint", pdata, "UintP", hwnd, "Uint", 4, "Uint", 0) } else hwnd = 0 DllCall("VirtualFreeEx", "Uint", hProc, "Uint", pRB, "Uint", 0, "Uint", 0x8000) DllCall("CloseHandle", "Uint", hProc) ; Negative values indicate seperator items. (abs(btn_index) is the index) return btn_index > -1 ? btn_index+1 : 0 }Covered by Lexikos' default copyright license.
2007-09-04
[*:2pwkpp58]Replaced Click with WinActivate when the button's associated window is known.
[*:2pwkpp58]Added support for Windows' "Group similar taskbar buttons" feature. If hover_task_group is set to 1, hovering over an item in a button-group menu will select it, and hovering over the taskbar will close the group menu.
[*:2pwkpp58]Added hover_task_min_info. If set to 1, hovering over the button of a minimized window will show a tooltip instead of activating it. (Ideas for more useful information in the tooltip are welcome.)
[*:2pwkpp58]Added hover_no_menus. If set to 1, hovering will have no effect if a menu (ahk_class #32768) is open.
[*:2pwkpp58]Added primitive z-order preservation, via hover_keep_zorder. Currently windows are still brought forward when activated, but the script attempts to move them back to where they were.
[*:2pwkpp58]Replaced the second timer (hovering) with an A_TimeIdle check and gosub (in hovercheck.)
[*:2pwkpp58]Increased the frequency of hovercheck to 10ms. This should improve the accuracy of hover_delay.
[*:2pwkpp58]A few misc changes.2007-09-05
[*:2pwkpp58]GetMouseTaskButton() now uses TB_HITTEST instead of TB_GETHOTITEM, so should be more accurate.2007-09-23
[*:2pwkpp58]Changed default hover_task_min_info to 0 (off - hovering over the button of a minimized window will activate it.)
[*:2pwkpp58]Clicking a mouse button now prevents "hover", unless the mouse was moved even slightly.
[*:2pwkpp58]Hovering over the taskbar no longer activates it when hover_any_window is enabled and the mouse isn't over a taskbar button (and in any other case.)2007-09-24
[*:2pwkpp58]Replaced hover_no_menus with the DisableHover group. Added #32768 (standard menus) and MozillaDropShadowWindowClass (used by Firefox and Thunderbird menus.)2007-09-29
[*:2pwkpp58]Changed default hover_any_window back to 0 (off). It was left on unintentionally during testing.2008-12-30
[*:2pwkpp58]Added license info.2011-03-18
[*:2pwkpp58]Got basic hover_task_buttons and hover_start_button functionality working on Windows 7.