From fcc058b397db3ef74c4dd9ab833d4a9e38c4c444 Mon Sep 17 00:00:00 2001 From: Joshua Fuhs Date: Fri, 29 Jun 2012 22:20:15 -0400 Subject: [PATCH] BID --- src/Manager.ahk | 188 ++++++++++++++++++++++++++++++++++++++---------- src/View.ahk | 27 +++++-- 2 files changed, 172 insertions(+), 43 deletions(-) diff --git a/src/Manager.ahk b/src/Manager.ahk index 396d0dc..de1d25f 100644 --- a/src/Manager.ahk +++ b/src/Manager.ahk @@ -237,7 +237,7 @@ Manager_logViewLayout() { } Manager_logWindowInfo( w ) { - Local v, wndId, isWinFocus, isBugnActive, isFloating, isHidden, isDecorated, isResponsive, wndTitle, wndProc, wndClass, wndStyle, wndX, wndY, wndW, wndH, detect_state + Local v, wndId, isWinFocus, isBugnActive, isFloating, isHidden, isDecorated, isResponsive, isGhost, wndTitle, wndProc, wndClass, wndStyle, wndX, wndY, wndW, wndH, detect_state detect_state := A_DetectHiddenWindows DetectHiddenWindows, On @@ -269,19 +269,25 @@ Manager_logWindowInfo( w ) { WinGet, wndStyle, Style, ahk_id %w% WinGetPos, wndX, wndY, wndW, wndH, ahk_id %w% + If Manager_isGhost(w) + isGhost := "*" + Else + isGhost := " " + DetectHiddenWindows, %detect_state% ; Intentionally don't detect hidden windows here to see what Manager_hungTest does - If Manager_hungTest(w) + If Manager_isHung(w) isResponsive := " " Else isResponsive := "*" + - Log_bare(w . "`t" . isHidden . " " isWinFocus . " " . isBugnActive . " " . isFloating . " " . isDecorated . " " . isResponsive . " " . Manager_#%w%_monitor . "`t" . Manager_#%w%_tags . "`t" . wndX . "`t" . wndY . "`t" . wndW . "`t" . wndH . "`t" . wndStyle . "`t" . wndProc . " / " . wndClass . " / " . wndTitle) + Log_bare(w . "`t" . isHidden . " " isWinFocus . " " . isBugnActive . " " . isFloating . " " . isDecorated . " " . isResponsive . " " . isGhost . " " . Manager_#%w%_monitor . "`t" . Manager_#%w%_tags . "`t" . wndX . "`t" . wndY . "`t" . wndW . "`t" . wndH . "`t" . wndStyle . "`t" . wndProc . " / " . wndClass . " / " . wndTitle) } Manager_logHeader() { - Log_bare( "ID`t`tH W A F D R M`tTags`tX`tY`tW`tH`tStyle`t`tProc / Class / Title") + Log_bare( "ID`t`tH W A F D R G M`tTags`tX`tY`tW`tH`tStyle`t`tProc / Class / Title") } Manager_logViewWindowList() { @@ -321,6 +327,7 @@ Manager_logHelp() { Log_bare(" F - Floating") Log_bare(" D - Decorated") Log_bare(" R - Responsive") + Log_bare(" G - Ghost") Log_bare(" M - Monitor") Log_bare(" Tags - Bit-mask of the views in which the window is active") Log_bare(" X - Windows X position") @@ -360,21 +367,44 @@ Manager_loop(index, increment, lowerBound, upperBound) { ; pv - Preferred view ; wndId - Window to add to the manager. Manager_manage(pm, pv, wndId) { - Local a, c0, hideTitle, i, isDecorated, isFloating, isManaged, l, m, n, replace, search, tags + Local a, c0, hideTitle, i, isDecorated, isFloating, isManaged, l, m, n, replace, search, tags, body Local wndControlList0, wndId0, wndIds, wndX, wndY, wndWidth, wndHeight, wndProcessName If Not InStr(Manager_allWndIds, wndId ";") Manager_allWndIds .= wndId ";" - Manager_applyRules(wndId, isManaged, m, tags, isFloating, isDecorated, hideTitle) - If (m = 0) - m := pm - If (m < 0) - m := 1 - If (m > Manager_monitorCount) ; If the specified monitor is out of scope, set it to the max. monitor. - m := Manager_monitorCount - If (tags = 0) - tags := 1 << (pv - 1) + body := 0 + If Manager_isGhost( wndId ) { + Log_dbg_msg(2, "A window has given up the ghost (Ghost wndId: " . wndId . ")") + ; Ghosts need special attention. + ; Say a quick prayer and try to reattach it to its body. + body := Manager_findHung( wndId ) + If body { + isManaged := InStr(Manager_managedWndIds, body ";") + m := Manager_#%body%_monitor + tags := Manager_#%body%_tags + isDecorated := Manager_#%body%_isDecorated + isFloating := Manager_#%body%_isFloating + hideTitle := InStr(Bar_hideTitleWndIds, body ";") + } + Else { + Log_dbg_msg(1, "No body could be found for ghost wndId: " . wndId) + } + } + + ; Apply rules if the window is either a normal window or a ghost without a body. + If ( body = 0 ) { + Manager_applyRules(wndId, isManaged, m, tags, isFloating, isDecorated, hideTitle) + + If (m = 0) + m := pm + If (m < 0) + m := 1 + If (m > Manager_monitorCount) ; If the specified monitor is out of scope, set it to the max. monitor. + m := Manager_monitorCount + If (tags = 0) + tags := 1 << (pv - 1) + } WinGet, wndProcessName, ProcessName, ahk_id %wndId% If (wndProcessName = "chrome.exe") { @@ -395,7 +425,13 @@ Manager_manage(pm, pv, wndId) { Loop, % Config_viewCount If (Manager_#%wndId%_tags & 1 << A_Index - 1) { - View_addWnd(m, A_Index, wndId) + If (body) { + ; Try to position near the body. + View_ghostWnd(m, A_Index, body, wndId) + } + Else { + View_addWnd(m, A_Index, wndId) + } } If Not Config_showBorder @@ -456,19 +492,48 @@ HSHELL_WINDOWDESTROYED := 2 HSHELL_WINDOWACTIVATED := 4 ; At least title change. HSHELL_REDRAW := 6 +; The following two are seen when a hung window recovers. +; lParam notes the ghost process +HSHELL_WINDOWREPLACED := 13 +; lParam notes the recovered process +;14 ; Full-screen app activated? Root-privileged window activated? HSHELL_RUDEAPPACTIVATED := 32772 ; When a window is signalling an application update. WINDOW_NOTICE := 32774 +; +; Reliable messages and their meanings (note that any message may be missed if bug.n is hung): +; 1 - Window shown (shown ID) +; 2 - Window destroyed or hidden, same message for both (destroyed or hidden ID) +; 4 - Window activated via mouse, alt+tab, or hotkey (sometimes 32772, but always one of them) +; 6 - Window title change (ID of redrawn window) +; 13 - Hung window recovers and replaces ghost window (ghost window ID is provided) +; 14 - Hung window recovered (ID of previously hung window) +; 32772 - Window activated via mouse, alt+tab, or hotkey (sometimes 4, but always one of them) +; 32774 - Window is flashing due to some event, one message for each flash +; +; Indications of: +; New windows - cmd/shell may be starting a new window on message 6 +; Win+e indicates a new window with message 6 as long as the button +; presses are below a certain frequency. +; Message 1 may indicate a new window started from Windows Explorer +; There doesn't seem to be a reliable way to get all application starts. +; Closed windows - 13 always indicates closed ghost window +; 2 always indicates closed standard window +; Focus change - 4 or 32772 always catch this +; Window event - 6 indicates when title changes which can be used +; in the case of some applications, 32774 works for others +; Manager_onShellMessage(wParam, lParam) { Local a, isChanged, aWndClass, aWndHeight, aWndId, aWndTitle, aWndWidth, aWndX, aWndY, m, t, wndClass, wndId, wndIds, wndPName, wndTitle, x, y - Log_dbg_msg(2, "Manager_onShellMessage(wParam: " . wParam . ", lParam: " . lParam) - SetFormat, Integer, hex lParam := lParam+0 SetFormat, Integer, d + + Log_dbg_msg(2, "Manager_onShellMessage( wParam: " . wParam . ", lParam: " . lParam . " )") + WinGetClass, wndClass, ahk_id %lParam% WinGetTitle, wndTitle, ahk_id %lParam% WinGet, wndPName, ProcessName, ahk_id %lParam% @@ -484,6 +549,15 @@ Manager_onShellMessage(wParam, lParam) { Bar_updateTitle() } + If ( wParam = HSHELL_WINDOWREPLACED ) { + ; This shouldn't need a redraw because the window was supposedly replaced. + Manager_unmanage(lParam) + } + + If ( wParam = 14 ) { + ; Window recovered from being hung. Maybe force a redraw. + } + If (wParam = 1 Or wParam = 2 Or wParam = 4 Or wParam = 6 Or wParam = 32772) And lParam And Not Manager_hideShow And Not Manager_focus { If Not (wParam = 4 Or wParam = 32772) If Not wndClass And Not (wParam = 2) { @@ -513,8 +587,6 @@ Manager_onShellMessage(wParam, lParam) { If wndIds { If (Config_onActiveHiddenWnds = "view") { - ; Grab the first of such windows and make it visible. - ; All others get forgotten (until the next round?) wndId := SubStr(wndIds, 1, InStr(wndIds, ";") - 1) Loop, % Config_viewCount If (Manager_#%wndId%_tags & 1 << A_Index - 1) { @@ -646,8 +718,11 @@ Manager_sync(ByRef wndIds = "") { flag := Manager_manage(Manager_aMonitor, Monitor_#%Manager_aMonitor%_aView_#1, wndId%A_Index%) If flag a := flag - } Else + } Else If Not Manager_isHung(wndId%A_Index%) { + ; This is a window that is already managed but was brought into focus by something. Maybe it + ; would be useful to do something with it. wndIds .= wndId%A_Index% ";" + } } visibleWndIds := visibleWndIds wndId%A_Index% ";" } @@ -738,7 +813,7 @@ Manager_unmanage(wndId) { } Manager_winActivate(wndId) { - Local wndHeight, wndWidth, wndX, wndY + Local wndHeight, wndWidth, wndX, wndY, newWnd If Config_mouseFollowsFocus { If wndId { @@ -747,16 +822,23 @@ Manager_winActivate(wndId) { } Else DllCall("SetCursorPos", "Int", Round(Monitor_#%Manager_aMonitor%_x + Monitor_#%Manager_aMonitor%_width / 2), "Int", Round(Monitor_#%Manager_aMonitor%_y + Monitor_#%Manager_aMonitor%_height / 2)) } - If Manager_hungTest(wndId) - Log_msg("Manager_winActivate: Potentially hung window " . wndId) - Else + If Manager_isHung(wndId) { + Log_dbg_msg(2, "Manager_winActivate: Potentially hung window " . wndId) + Return 1 + } + Else { WinActivate, ahk_id %wndId% + WinGet, newWin, ID, A + If (wndId != newWin) + Return 1 + } Bar_updateTitle() + Return 0 } Manager_winMove(wndId, x, y, width, height) { - If Manager_hungTest(wndId) { - Log_msg("Manager_winMove: Potentially hung window " . wndId) + If Manager_isHung(wndId) { + Log_dbg_msg(2, "Manager_winMove: Potentially hung window " . wndId) Return 1 } Else @@ -765,7 +847,7 @@ Manager_winMove(wndId, x, y, width, height) { WM_EXITSIZEMOVE = 0x0232 SendMessage, WM_ENTERSIZEMOVE, , , , ahk_id %wndId% If ErrorLevel { - Log_msg("Manager_winMove: Potentially hung window " . wndId) + Log_dbg_msg(2, "Manager_winMove: Potentially hung window " . wndId) Return 1 } Else { @@ -776,8 +858,8 @@ Manager_winMove(wndId, x, y, width, height) { Manager_winHide(wndId) { - If Manager_hungTest(wndId) { - Log_msg("Manager_winHide: Potentially hung window " . wndId) + If Manager_isHung(wndId) { + Log_dbg_msg(2, "Manager_winHide: Potentially hung window " . wndId) Return 1 } Else { @@ -788,8 +870,8 @@ Manager_winHide(wndId) { Manager_winShow(wndId) { - If Manager_hungTest(wndId) { - Log_msg("Manager_winShow: Potentially hung window " . wndId) + If Manager_isHung(wndId) { + Log_dbg_msg(2, "Manager_winShow: Potentially hung window " . wndId) Return 1 } Else { @@ -800,8 +882,8 @@ Manager_winShow(wndId) { Manager_winClose(wndId) { - If Manager_hungTest(wndId) { - Log_msg("Manager_winClose: Potentially hung window " . wndId) + If Manager_isHung(wndId) { + Log_dbg_msg(2, "Manager_winClose: Potentially hung window " . wndId) Return 1 } Else { @@ -812,8 +894,8 @@ Manager_winClose(wndId) { Manager_winSet(type, value, wndId) { - If Manager_hungTest(wndId) { - Log_msg("Manager_winSet: Potentially hung window " . wndId) + If Manager_isHung(wndId) { + Log_dbg_msg(2, "Manager_winSet: Potentially hung window " . wndId) Return 1 } Else { @@ -824,7 +906,7 @@ Manager_winSet(type, value, wndId) { ; 0 - Not hung ; 1 - Hung -Manager_hungTest(wndId) { +Manager_isHung(wndId) { Local result, detect_setting, WM_NULL WM_NULL := 0 detect_setting := A_DetectHiddenWindows @@ -837,4 +919,38 @@ Manager_hungTest(wndId) { Return 1 Else Return 0 +} + +; Given a ghost window, try to find its body. +; This is only known to work on Windows 7 +Manager_findHung( ghostWnd ) { + Local expectedTitle, expectedX, expectedY, expectedW, expectedH, wndTitle, wndX, wndY, wndW, wndH, wndIds + ;Log_dbg_msg(3, "Manager_findHung(" . ghostWnd . ")") + WinGetTitle, expectedTitle, ahk_id %ghostWnd% + StringReplace, expectedTitle, expectedTitle, " (Not Responding)", "" + WinGetPos, expectedX, expectedY, expectedW, expectedH, ahk_id %ghostWnd% + + SetTitleMatchMode, 2 + WinGet, wndIds, List, %expectedTitle% + Loop, % wndIds { + If (A_Index = ghostWnd) + Continue + WinGetPos, wndX, wndY, wndW, wndH, % "ahk_id" wndIDs%A_Index% + + If (wndX = expectedX) And (wndY = expectedY) And (wndW = expectedW) And (wndH = expectedH) + Return wndIds%A_Index% + } + Return 0 +} + +Manager_isGhost(wndId) { + Local wndClass, wndProc + + WinGet, wndProc, ProcessName, ahk_id %wndId% + WinGetClass, wndClass, ahk_id %wndId% + + If (wndProc = "dwm.exe") And (wndClass = "Ghost") + Return 1 + Else + Return 0 } \ No newline at end of file diff --git a/src/View.ahk b/src/View.ahk index df8858a..ce54aea 100644 --- a/src/View.ahk +++ b/src/View.ahk @@ -36,7 +36,7 @@ View_init(m, v) { } View_activateWindow(d) { - Local aWndId, i, j, v, wndId, wndId0, wndIds, success, direction + Local aWndId, i, j, v, wndId, wndId0, wndIds, failure, direction Log_dbg_msg(1, "View_activateWindow(" . d . ")") @@ -51,6 +51,8 @@ View_activateWindow(d) { StringSplit, wndId, wndIds, `; Log_dbg_bare(2, "wndId count: " . wndId0) If (wndId0 > 1) { + If Manager_#%aWndId%_isFloating + Manager_winSet("Bottom", "", aWndId) Loop, % wndId0 If (wndId%A_Index% = aWndId) { i := A_Index @@ -66,14 +68,17 @@ View_activateWindow(d) { Log_dbg_bare(2, "Next wndId index: " . j) wndId := wndId%j% Manager_winSet("AlwaysOnTop", "On", wndId) - success := Manager_winSet("AlwaysOnTop", "Off", wndId) - If (success = 0) + Manager_winSet("AlwaysOnTop", "Off", wndId) + ; This is a lot of extra work in case there are hung windows on the screen. + ; We still want to be able to cycle through them. + failure := Manager_winActivate(wndId) + If Not failure { Break + } + j := Manager_loop(j, direction, 1, wndId0) } - If Manager_#%aWndId%_isFloating - Manager_winSet("Bottom", "", aWndId) - Manager_winActivate(wndId) + } } @@ -115,6 +120,14 @@ View_addWnd(m, v, wndId) { View_#%m%_#%v%_wndIds := wndId ";" View_#%m%_#%v%_wndIds } +View_ghostWnd(m, v, bodyWndId, ghostWndId) { + Local search, replace + + search := bodyWndId ";" + replace := search ghostWndId ";" + StringReplace, View_#%m%_#%v%_wndIds, View_#%m%_#%v%_wndIds, %search%, %replace% +} + ; Remove a window from the view in question. View_delWnd(m, v, wndId) { StringReplace, View_#%m%_#%v%_wndIds, View_#%m%_#%v%_wndIds, %wndId%`;, @@ -140,7 +153,7 @@ View_getTiledWndIds(m, v, ByRef tiledWndIds) { StringTrimRight, wndIds, View_#%m%_#%v%_wndIds, 1 Loop, PARSE, wndIds, `; { - If Not Manager_#%A_LoopField%_isFloating And WinExist("ahk_id " A_LoopField) { + If Not Manager_#%A_LoopField%_isFloating And WinExist("ahk_id " A_LoopField) and Not Manager_isHung(A_LoopField) { n += 1 tiledWndIds .= A_LoopField ";" }