Periodic state save and restore work

This commit is contained in:
Joshua Fuhs 2012-11-25 08:56:35 -05:00
parent be8533624e
commit af151b93a9
4 changed files with 418 additions and 125 deletions

View file

@ -96,8 +96,9 @@ Config_init() {
Config_autoSaveSession := False ; Automatically save the current state of monitors, views, layouts (active view, layout, axes, mfact and msplit) to the configuration file (set below) when quitting bug.n. Config_autoSaveSession := False ; Automatically save the current state of monitors, views, layouts (active view, layout, axes, mfact and msplit) to the configuration file (set below) when quitting bug.n.
If Not Config_filePath ; The file path, to which the configuration and session is saved. This target directory must be writable by the user (%A_ScriptDir% is the diretory, in which "Main.ahk" or the executable of bug.n is saved). If Not Config_filePath ; The file path, to which the configuration and session is saved. This target directory must be writable by the user (%A_ScriptDir% is the diretory, in which "Main.ahk" or the executable of bug.n is saved).
Config_filePath := A_ScriptDir "\Config.ini" Config_filePath := A_ScriptDir "\Config.ini"
Config_maintenanceInterval := 5000
Config_restore("Config") Config_restoreConfig(Config_filePath)
Config_getSystemSettings() Config_getSystemSettings()
Config_initColors() Config_initColors()
Loop, % Config_layoutCount { Loop, % Config_layoutCount {
@ -281,96 +282,103 @@ Config_redirectHotkey(key) {
} }
} }
Config_restore(section, m = 0) { Config_restoreLayout(filename, m) {
Local cmd, i, key, type, val, var Local i, var, val
If FileExist(Config_filePath) { If Not FileExist(filename)
If (section = "Config") { Return
Loop, READ, %Config_filePath%
If (SubStr(A_LoopReadLine, 1, 7) = "Config_") { Loop, READ, %filename%
i := InStr(A_LoopReadLine, "=") If (SubStr(A_LoopReadLine, 1, 10 + StrLen(m)) = "Monitor_#" m "_" Or SubStr(A_LoopReadLine, 1, 8 + StrLen(m)) = "View_#" m "_#") {
var := SubStr(A_LoopReadLine, 1, i - 1) i := InStr(A_LoopReadLine, "=")
val := SubStr(A_LoopReadLine, i + 1) var := SubStr(A_LoopReadLine, 1, i - 1)
type := SubStr(var, 1, 13) val := SubStr(A_LoopReadLine, i + 1)
If (type = "Config_hotkey") { %var% := val
i := InStr(val, "::")
key := SubStr(val, 1, i - 1)
cmd := SubStr(val, i + 2)
If Not cmd
Hotkey, %key%, Off
Else {
Config_hotkeyCount += 1
Config_hotkey_#%Config_hotkeyCount%_key := key
Config_hotkey_#%Config_hotkeyCount%_command := cmd
Hotkey, %key%, Config_hotkeyLabel
}
} Else If (type = "Config_rule") {
i := 0
If InStr(var, "Config_rule_#")
i := SubStr(var, 14)
If (i = 0 Or i > Config_ruleCount) {
Config_ruleCount += 1
i := Config_ruleCount
}
var := "Config_rule_#" i
}
%var% := val
}
} Else If (section = "Monitor") {
Loop, READ, %Config_filePath%
If (SubStr(A_LoopReadLine, 1, 10 + StrLen(m)) = "Monitor_#" m "_" Or SubStr(A_LoopReadLine, 1, 8 + StrLen(m)) = "View_#" m "_#") {
i := InStr(A_LoopReadLine, "=")
var := SubStr(A_LoopReadLine, 1, i - 1)
val := SubStr(A_LoopReadLine, i + 1)
%var% := val
}
} }
}
} }
Config_saveSession() { Config_restoreConfig(filename) {
Local m, text Local cmd, i, key, type, val, var
If Not FileExist(filename)
Return
Loop, READ, %filename%
If (SubStr(A_LoopReadLine, 1, 7) = "Config_") {
Log_msg("Processing line: " . A_LoopReadLine)
i := InStr(A_LoopReadLine, "=")
var := SubStr(A_LoopReadLine, 1, i - 1)
val := SubStr(A_LoopReadLine, i + 1)
type := SubStr(var, 1, 13)
If (type = "Config_hotkey") {
i := InStr(val, "::")
key := SubStr(val, 1, i - 1)
cmd := SubStr(val, i + 2)
If Not cmd
Hotkey, %key%, Off
Else {
Config_hotkeyCount += 1
Config_hotkey_#%Config_hotkeyCount%_key := key
Config_hotkey_#%Config_hotkeyCount%_command := cmd
Hotkey, %key%, Config_hotkeyLabel
}
} Else If (type = "Config_rule") {
i := 0
If InStr(var, "Config_rule_#")
i := SubStr(var, 14)
If (i = 0 Or i > Config_ruleCount) {
Config_ruleCount += 1
i := Config_ruleCount
}
var := "Config_rule_#" i
}
%var% := val
}
}
Config_UI_saveSession() {
Config_saveSession(Config_filePath)
}
Config_saveSession(filename) {
Local m, text, tmpfilename
tmpfilename := filename . ".tmp"
FileDelete, %tmpfilename%
text := "; bug.n - tiling window management`n; @version " VERSION "`n`n" text := "; bug.n - tiling window management`n; @version " VERSION "`n`n"
If FileExist(Config_filePath) { If FileExist(filename) {
Loop, READ, %Config_filePath% Loop, READ, %filename%
If (SubStr(A_LoopReadLine, 1, 7) = "Config_") If (SubStr(A_LoopReadLine, 1, 7) = "Config_")
text .= A_LoopReadLine "`n" text .= A_LoopReadLine "`n"
text .= "`n" text .= "`n"
} }
FileDelete, %Config_filePath%
Loop, % Manager_monitorCount { Loop, % Manager_monitorCount {
m := A_Index m := A_Index
If Not (Monitor_#%m%_aView_#1 = 1) text .= "Monitor_#" m "_aView_#1=" Monitor_#%m%_aView_#1 "`n"
text .= "Monitor_#" m "_aView_#1=" Monitor_#%m%_aView_#1 "`n" text .= "Monitor_#" m "_aView_#2=" Monitor_#%m%_aView_#2 "`n"
If Not (Monitor_#%m%_aView_#2 = 1) text .= "Monitor_#" m "_showBar=" Monitor_#%m%_showBar "`n"
text .= "Monitor_#" m "_aView_#2=" Monitor_#%m%_aView_#2 "`n"
If Not (Monitor_#%m%_showBar = Config_showBar)
text .= "Monitor_#" m "_showBar=" Monitor_#%m%_showBar "`n"
Loop, % Config_viewCount { Loop, % Config_viewCount {
If Not (View_#%m%_#%A_Index%_layout_#1 = 1) text .= "View_#" m "_#" A_Index "_layout_#1=" View_#%m%_#%A_Index%_layout_#1 "`n"
text .= "View_#" m "_#" A_Index "_layout_#1=" View_#%m%_#%A_Index%_layout_#1 "`n" text .= "View_#" m "_#" A_Index "_layout_#2=" View_#%m%_#%A_Index%_layout_#2 "`n"
If Not (View_#%m%_#%A_Index%_layout_#2 = 1) text .= "View_#" m "_#" A_Index "_layoutAxis_#1=" View_#%m%_#%A_Index%_layoutAxis_#1 "`n"
text .= "View_#" m "_#" A_Index "_layout_#2=" View_#%m%_#%A_Index%_layout_#2 "`n" text .= "View_#" m "_#" A_Index "_layoutAxis_#2=" View_#%m%_#%A_Index%_layoutAxis_#2 "`n"
If Not (View_#%m%_#%A_Index%_layoutAxis_#1 = Config_layoutAxis_#1) text .= "View_#" m "_#" A_Index "_layoutAxis_#3=" View_#%m%_#%A_Index%_layoutAxis_#3 "`n"
text .= "View_#" m "_#" A_Index "_layoutAxis_#1=" View_#%m%_#%A_Index%_layoutAxis_#1 "`n" text .= "View_#" m "_#" A_Index "_layoutGapWidth=" View_#%m%_#%A_Index%_layoutGapWidth "`n"
If Not (View_#%m%_#%A_Index%_layoutAxis_#2 = Config_layoutAxis_#2) text .= "View_#" m "_#" A_Index "_layoutMFact=" View_#%m%_#%A_Index%_layoutMFact "`n"
text .= "View_#" m "_#" A_Index "_layoutAxis_#2=" View_#%m%_#%A_Index%_layoutAxis_#2 "`n" text .= "View_#" m "_#" A_Index "_layoutMX=" View_#%m%_#%A_Index%_layoutMX "`n"
If Not (View_#%m%_#%A_Index%_layoutAxis_#3 = Config_layoutAxis_#3) text .= "View_#" m "_#" A_Index "_layoutMY=" View_#%m%_#%A_Index%_layoutMY "`n"
text .= "View_#" m "_#" A_Index "_layoutAxis_#3=" View_#%m%_#%A_Index%_layoutAxis_#3 "`n"
If Not (View_#%m%_#%A_Index%_layoutGapWidth = Config_layoutGapWidth)
text .= "View_#" m "_#" A_Index "_layoutGapWidth=" View_#%m%_#%A_Index%_layoutGapWidth "`n"
If Not (View_#%m%_#%A_Index%_layoutMFact = Config_layoutMFactor)
text .= "View_#" m "_#" A_Index "_layoutMFact=" View_#%m%_#%A_Index%_layoutMFact "`n"
If Not (View_#%m%_#%A_Index%_layoutMX = 1)
text .= "View_#" m "_#" A_Index "_layoutMX=" View_#%m%_#%A_Index%_layoutMX "`n"
If Not (View_#%m%_#%A_Index%_layoutMY = 1)
text .= "View_#" m "_#" A_Index "_layoutMY=" View_#%m%_#%A_Index%_layoutMY "`n"
} }
} }
FileAppend, %text%, %Config_filePath% FileAppend, %text%, %tmpfilename%
If ErrorLevel {
If FileExist(tmpfilename)
FileDelete, %tmpfilename%
}
Else
FileMove, %tmpfilename%, %filename%, 1
} }
/** /**
@ -457,7 +465,7 @@ Config_saveSession() {
#Space::Monitor_toggleTaskBar() ; Hide / Show the task bar. #Space::Monitor_toggleTaskBar() ; Hide / Show the task bar.
#y::Bar_toggleCommandGui() ; Open the command GUI for executing programmes or bug.n functions. #y::Bar_toggleCommandGui() ; Open the command GUI for executing programmes or bug.n functions.
#^e::Run, edit %Config_filePath% ; Open the configuration file in the standard text editor. #^e::Run, edit %Config_filePath% ; Open the configuration file in the standard text editor.
#^s::Config_saveSession() ; Save the current state of monitors, views, layouts to the configuration file. #^s::Config_UI_saveSession() ; Save the current state of monitors, views, layouts to the configuration file.
#^r::Main_reload() ; Reload bug.n (i. e. the configuration and its dependent settings) without deleting the window lists of bug.n and restoring windows. #^r::Main_reload() ; Reload bug.n (i. e. the configuration and its dependent settings) without deleting the window lists of bug.n and restoring windows.
; It does not reset internal configuration variables, the tray icon or menu, hotkeys (unless set explicitly in Config.ini), individual window settings like Config_showBorder (since windows might be hidden) or hiding the title bar, the monitor count or views. ; It does not reset internal configuration variables, the tray icon or menu, hotkeys (unless set explicitly in Config.ini), individual window settings like Config_showBorder (since windows might be hidden) or hiding the title bar, the monitor count or views.
; It does not reload functions. Changed rules are only applied to new windows. ; It does not reload functions. Changed rules are only applied to new windows.

View file

@ -33,20 +33,9 @@ SetWinDelay, 10
; pseudo main function ; pseudo main function
Main_setup()
EnvGet, appDir, APPDATA Log_init(Main_logFile, False)
bugnDir := appDir . "\bug.n"
IfNotExist, %bugnDir%
FileCreateDir, %bugnDir%
FileGetAttrib, attrib, %bugnDir%
IfNotInString, attrib, D
{
MsgBox, The file path '%appDir%' already exists and is not a directory. Aborting.
Return
}
logFile := bugnDir . "\bugn_log.txt"
Log_init(logFile, False)
Log_msg("====== Initializing ======") Log_msg("====== Initializing ======")
If 0 = 1 If 0 = 1
@ -71,7 +60,7 @@ Return ; end of the auto-execute section
Main_cleanup: ; The labels with "ExitApp" or "Return" at the end and hotkeys have to be after the auto-execute section. Main_cleanup: ; The labels with "ExitApp" or "Return" at the end and hotkeys have to be after the auto-execute section.
Log_msg("====== Cleaning up ======") Log_msg("====== Cleaning up ======")
If Config_autoSaveSession If Config_autoSaveSession
Config_saveSession() Config_saveSession(Config_filePath)
Manager_cleanup() Manager_cleanup()
DllCall("CloseHandle", "UInt", Bar_hDrive) ; used in Bar_getDiskLoad DllCall("CloseHandle", "UInt", Bar_hDrive) ; used in Bar_getDiskLoad
ExitApp ExitApp
@ -84,6 +73,49 @@ Main_quit:
ExitApp ExitApp
Return Return
; Create bug.n-specific directories.
Main_makeDir(dirName) {
IfNotExist, %dirName%
{
FileCreateDir, %dirName%
If ErrorLevel {
MsgBox, Error (%ErrorLevel%) when creating '%dirName%'. Aborting.
ExitApp
}
}
Else {
FileGetAttrib, attrib, %dirName%
IfNotInString, attrib, D
{
MsgBox, The file path '%dirName%' already exists and is not a directory. Aborting.
ExitApp
}
}
}
Main_setup() {
Local winAppDir
Main_appDir := ""
Main_logFile := ""
Main_dataDir := ""
Main_autoLayout := ""
Main_autoWindowState := ""
EnvGet, winAppDir, APPDATA
Main_appDir := winAppDir . "\bug.n"
Main_logFile := Main_appDir . "\bugn_log.txt"
Main_dataDir := Main_appDir . "\data"
Main_autoLayout := Main_dataDir . "\_Layout.ini"
Main_autoWindowState := Main_dataDir . "\_WindowState.ini"
Main_makeDir(Main_appDir)
Main_makeDir(Main_dataDir)
}
Main_reload() { Main_reload() {
Local i, ncm, ncmSize Local i, ncm, ncmSize

View file

@ -42,6 +42,10 @@ Manager_init() {
} }
Bar_getHeight() Bar_getHeight()
; axes, dimensions, percentage, flipped, gapWidth
Manager_layoutDirty := 0
; New/closed windows, active changed,
Manager_windowsDirty := 0
Manager_aMonitor := 1 Manager_aMonitor := 1
Manager_taskBarMonitor := "" Manager_taskBarMonitor := ""
Manager_showTaskBar := True Manager_showTaskBar := True
@ -67,9 +71,41 @@ Manager_init() {
} }
Manager_registerShellHook() Manager_registerShellHook()
SetTimer, Manager_maintenance_label, %Config_maintenanceInterval%
SetTimer, Bar_loop, %Config_readinInterval% SetTimer, Bar_loop, %Config_readinInterval%
} }
; Asynchronous management of various WM properties.
; We want to make sure that we can recover the layout and windows in the event of
; unexpected problems.
; Periodically check for changes to these things and save them somewhere (not over
; user-defined files).
Manager_maintenance_label:
Manager_maintenance()
Return
Manager_maintenance() {
Local tmp
;Log_dbg_msg(2, "Manager_maintenance")
; @todo: Check for changes to the layout.
;If Manager_layoutDirty {
;Log_msg("Saving layout state: " . Main_autoLayout)
Config_saveSession(Main_autoLayout)
Manager_layoutDirty := 0
;}
; @todo: Manager_sync
; @todo: Check for changes to windows.
;If Manager_windowsDirty {
;Log_msg("Saving window state: " . Main_autoWindowState)
Manager_saveWindowState(Main_autoWindowState, Manager_monitorCount, Config_viewCount)
Manager_windowsDirty := 0
;}
}
Manager_activateMonitor(d) { Manager_activateMonitor(d) {
Local aView, aWndClass, aWndHeight, aWndId, aWndWidth, aWndX, aWndY, v, wndId Local aView, aWndClass, aWndHeight, aWndId, aWndWidth, aWndX, aWndY, v, wndId
@ -361,6 +397,42 @@ Manager_loop(index, increment, lowerBound, upperBound) {
Return, index Return, index
} }
Manager__setWinProperties(wndId, isManaged, m, tags, isDecorated, isFloating, hideTitle ) {
Local a
If Not Instr(Manager_allWndIds, wndId ";")
Manager_allWndIds .= wndId ";"
If (isManaged) {
Manager_managedWndIds .= wndId ";"
Monitor_moveWindow(m, wndId)
Manager_#%wndId%_tags := tags
Manager_#%wndId%_isDecorated := isDecorated
Manager_#%wndId%_isFloating := isFloating
If Not Config_showBorder
Manager_winSet("Style", "-0x40000", wndId)
If Not Manager_#%wndId%_isDecorated
Manager_winSet("Style", "-0xC00000", wndId)
a := Manager_#%wndId%_tags & (1 << (Monitor_#%m%_aView_#1 - 1))
If a {
Manager_aMonitor := m
Manager_winActivate(wndId)
} Else {
Manager_hideShow := True
Manager_winHide(wndId)
Manager_hideShow := False
}
}
If hideTitle
Bar_hideTitleWndIds .= wndId . ";"
Return, a
}
; Accept a window to be added to the system for management. ; Accept a window to be added to the system for management.
; Provide a monitor, view preference, but don't override the config. ; Provide a monitor, view preference, but don't override the config.
; pm - Preferred monitor ; pm - Preferred monitor
@ -370,8 +442,10 @@ Manager_manage(pm, pv, wndId) {
Local a, c0, hideTitle, i, isDecorated, isFloating, isManaged, l, m, n, replace, search, tags, body Local a, c0, hideTitle, i, isDecorated, isFloating, isManaged, l, m, n, replace, search, tags, body
Local wndControlList0, wndId0, wndIds, wndX, wndY, wndWidth, wndHeight, wndProcessName Local wndControlList0, wndId0, wndIds, wndX, wndY, wndWidth, wndHeight, wndProcessName
If Not InStr(Manager_allWndIds, wndId ";") ; Manage any window only once.
Manager_allWndIds .= wndId ";" If InStr(Manager_managedWndIds, wndId ";")
Return
body := 0 body := 0
If Manager_isGhost( wndId ) { If Manager_isGhost( wndId ) {
@ -406,6 +480,7 @@ Manager_manage(pm, pv, wndId) {
tags := 1 << (pv - 1) tags := 1 << (pv - 1)
} }
; @todo: Remove this application-specific code.
WinGet, wndProcessName, ProcessName, ahk_id %wndId% WinGet, wndProcessName, ProcessName, ahk_id %wndId%
If (wndProcessName = "chrome.exe") { If (wndProcessName = "chrome.exe") {
WinGet, wndControlList, ControlList, ahk_id %wndId% WinGet, wndControlList, ControlList, ahk_id %wndId%
@ -414,17 +489,12 @@ Manager_manage(pm, pv, wndId) {
isManaged := False isManaged := False
} }
a := Manager__setWinProperties( wndId, isManaged, m, tags, isDecorated, isFloating, hideTitle)
; Do view placement.
If isManaged { If isManaged {
Monitor_moveWindow(m, wndId)
Manager_managedWndIds .= wndId ";"
Manager_#%wndId%_monitor := m
Manager_#%wndId%_tags := tags
Manager_#%wndId%_isDecorated := isDecorated
Manager_#%wndId%_isFloating := isFloating
Loop, % Config_viewCount Loop, % Config_viewCount
If (Manager_#%wndId%_tags & 1 << A_Index - 1) { If (Manager_#%wndId%_tags & (1 << (A_Index - 1))) {
If (body) { If (body) {
; Try to position near the body. ; Try to position near the body.
View_ghostWnd(m, A_Index, body, wndId) View_ghostWnd(m, A_Index, body, wndId)
@ -433,28 +503,8 @@ Manager_manage(pm, pv, wndId) {
View_addWnd(m, A_Index, wndId) View_addWnd(m, A_Index, wndId)
} }
} }
If Not Config_showBorder
Manager_winSet("Style", "-0x40000", wndId)
If Not Manager_#%wndId%_isDecorated
Manager_winSet("Style", "-0xC00000", wndId)
a := Manager_#%wndId%_tags & 1 << Monitor_#%m%_aView_#1 - 1
If a {
Manager_aMonitor := m
Manager_winActivate(wndId)
} Else {
Manager_hideShow := True
Manager_winHide(wndId)
Manager_hideShow := False
}
} }
If hideTitle And Not InStr(Bar_hideTitleWndIds, wndId)
Bar_hideTitleWndIds .= wndId . ";"
Else If Not hideTitle
StringReplace, Bar_hideTitleWndIds, Bar_hideTitleWndIds, %wndId%`;,
Return, a Return, a
} }
@ -524,6 +574,7 @@ WINDOW_NOTICE := 32774
; Focus change - 4 or 32772 always catch this ; Focus change - 4 or 32772 always catch this
; Window event - 6 indicates when title changes which can be used ; Window event - 6 indicates when title changes which can be used
; in the case of some applications, 32774 works for others ; in the case of some applications, 32774 works for others
; Windows events can't always be caught.
; ;
Manager_onShellMessage(wParam, lParam) { Manager_onShellMessage(wParam, lParam) {
Local a, isChanged, aWndClass, aWndHeight, aWndId, aWndTitle, aWndWidth, aWndX, aWndY, m, t, wndClass, wndId, wndIds, wndPName, wndTitle, x, y Local a, isChanged, aWndClass, aWndHeight, aWndId, aWndTitle, aWndWidth, aWndX, aWndY, m, t, wndClass, wndId, wndIds, wndPName, wndTitle, x, y
@ -558,6 +609,13 @@ Manager_onShellMessage(wParam, lParam) {
; Window recovered from being hung. Maybe force a redraw. ; Window recovered from being hung. Maybe force a redraw.
} }
; @todo: There are two problems with the use of Manager_hideShow:
; 1) If Manager_hideShow is set when we hit this block, we won't take some actions that should eventually be taken.
; This _may_ explain why some windows never get picked up when spamming Win+e
; 2) There is a race condition between the time that Manager_hideShow is checked and any other action which we are
; trying to protect against. If another process (hotkey) enters a hideShow block after Manager_hideShow has
; been checked here, bad things could happen. I've personally observed that windows may be permanently hidden.
; Look into the use of AHK synchronization primitives.
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 (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 (wParam = 4 Or wParam = 32772)
If Not wndClass And Not (wParam = 2) { If Not wndClass And Not (wParam = 2) {
@ -703,6 +761,8 @@ Manager_sizeWindow() {
SendMessage, WM_SYSCOMMAND, SC_SIZE, , , ahk_id %aWndId% SendMessage, WM_SYSCOMMAND, SC_SIZE, , , ahk_id %aWndId%
} }
; @todo: This constantly tries to re-add windows that are never going to be manageable.
; Manager_manage should probably ignore all windows that are already in Manager_allWndIds.
Manager_sync(ByRef wndIds = "") { Manager_sync(ByRef wndIds = "") {
Local a, flag, shownWndIds, v, visibleWndIds, wndId Local a, flag, shownWndIds, v, visibleWndIds, wndId
@ -727,6 +787,7 @@ Manager_sync(ByRef wndIds = "") {
visibleWndIds := visibleWndIds wndId%A_Index% ";" visibleWndIds := visibleWndIds wndId%A_Index% ";"
} }
; @todo-future: Find out why this unmanage code exists and if it's still needed.
; check, if a window, that is known to be visible, is actually not visible ; check, if a window, that is known to be visible, is actually not visible
StringTrimRight, shownWndIds, shownWndIds, 1 StringTrimRight, shownWndIds, shownWndIds, 1
Loop, PARSE, shownWndIds, `; Loop, PARSE, shownWndIds, `;
@ -741,6 +802,189 @@ Manager_sync(ByRef wndIds = "") {
Return, a Return, a
} }
Manager_saveWindowState(filename, nm, nv) {
Local allWndId, allWndIds, process, title, text, monitor, wndId, view, isManaged, isTitleHidden
text := "; bug.n - tiling window management`n; @version " VERSION "`n`n"
tmpfname := filename . ".tmp"
FileDelete, %tmpfname%
; Dump window ID and process name. If these two don't match an existing process, we won't try
; to recover that window.
StringTrimRight, allWndIds, Manager_allWndIds, 1
StringSplit, allWndId, allWndIds, `;
DetectHiddenWindows, On
Loop, % allWndId0 {
wndId := allWndId%A_Index%
WinGet, process, ProcessName, ahk_id %wndId%
; Include title for informative reasons.
WinGetTitle, title, ahk_id %wndId%
; wndId;process;Tags;Floating;Decorated;HideTitle;Managed;Title
If ( InStr(Manager_managedWndIds, wndId . ";") > 0 ) {
isManaged := 1
}
else {
isManaged := 0
}
If ( InStr(Bar_hideTitleWndIds, wndId . ";") > 0 )
isTitleHidden := 1
Else
isTitleHidden := 0
text .= "Window " . wndId . ";" . process . ";" . Manager_#%wndId%_monitor . ";" . Manager_#%wndId%_tags . ";" . Manager_#%wndId%_isFloating . ";" . Manager_#%wndId%_isDecorated . ";" . isTitleHidden . ";" . isManaged . ";" . title . "`n"
}
DetectHiddenWindows, Off
text .= "`n"
; @todo: Dump window arrangements on every view. If some views or monitors have disappeared, leave their
; corresponding windows alone.
Loop, % nm {
monitor := A_Index
Loop, % nv {
view := A_Index
; @todo: Dump all view window lists
text .= "View_#" . monitor . "_#" . view . "_wndIds=" . View_#%monitor%_#%view%_wndIds . "`n"
}
}
FileAppend, %text%, %tmpfname%
If ErrorLevel {
If FileExist(tmpfname)
FileDelete, %tmpfname%
}
Else
FileMove, %tmpfname%, %filename%, 1
}
; Restore previously saved window state.
; If the state is completely different, this function won't do much. However, if restoring from a crash
; or simply restarting bug.n, it should completely recover the window state.
Manager__restoreWindowState(filename) {
Local vidx, widx, i, j, m, v, candidate_set, view_set, excluded_view_set, view_m0, view_v0, view_list0, wnds0, items0, wndProc, view_var, isManaged, isFloating, isDecorated, hideTitle
If Not FileExist(filename)
Return
widx := 1
vidx := 1
view_set := ""
excluded_view_set := ""
; Read all interesting things from the file.
Loop, READ, %filename%
If (SubStr(A_LoopReadLine, 1, 5) = "View_") {
i := InStr(A_LoopReadLine, "#")
j := InStr(A_LoopReadLine, "_", false, i)
m := SubStr(A_LoopReadLine, i + 1, j - i - 1)
i := InStr(A_LoopReadLine, "#", false, j)
j := InStr(A_LoopReadLine, "_", false, i)
v := SubStr(A_LoopReadLine, i + 1, j - i - 1)
i := InStr(A_LoopReadLine, "=", j + 1)
If (m <= Manager_monitorCount) And ( v <= Config_viewCount ) {
view_list%vidx% := SubStr(A_LoopReadLine, i + 1)
view_m%vidx% := m
view_v%vidx% := v
view_set := view_set . view_list%vidx%
vidx := vidx + 1
}
Else {
excluded_view_set := excluded_view_set . view_list%vidx%
Log_msg("View (" . m . ", " . v . ") is no longer available (" . vidx . ")")
}
}
Else If (SubStr(A_LoopReadLine, 1, 7) = "Window ") {
wnds%widx% := SubStr(A_LoopReadLine, 8)
widx := widx + 1
}
;Log_msg("view_set: " . view_set)
;Log_msg("excluded_view_set: " . excluded_view_set)
candidate_set := List_new()
; Scan through all defined windows. Create a candidate set of windows based on whether the properties of existing windows match.
Loop, % (widx - 1) {
StringSplit, items, wnds%A_Index%, `;
If ( items0 < 9 ) {
Log_msg("Window '" . wnds%A_Index% . "' could not be processed due to parse error")
Continue
}
i := 1
i := items%i%
j := 2
DetectHiddenWindows, On
WinGet, wndProc, ProcessName, ahk_id %i%
DetectHiddenWindows, Off
If Not ( items%j% = wndProc ) {
Log_msg("Window ahk_id " . i . " process '" . wndProc . "' doesn't match expected '" . items%j% . "', forgetting this window")
Continue
}
j := 8
isManaged := items%j%
; If Managed
If ( items%j% )
If ( InStr(view_set, i) = 0) {
If ( InStr(excluded_view_set, i) ) {
Log_msg("Window ahk_id " . i . " is being ignored because it no longer belongs to an active view")
}
Else {
Log_msg("Window ahk_id " . i . " is being ignored because it doesn't exist in any views")
}
Continue
}
; Set up the window.
j := 3
m := items%j%
j := 4
v := items%j%
j := 5
isFloating := items%j%
j := 6
isDecorated := items%j%
j := 7
hideTitle := items%j%
Manager__setWinProperties(i, isManaged, m, v, isDecorated, isFloating, hideTitle )
;Manager_winHide(i)
List_append(candidate_set, i)
}
;Log_msg("candidate_set: " . candidate_set)
; Set up all views. Must filter the window list by those from the candidate set.
Loop, % (vidx - 1) {
StringSplit, items, view_list%A_Index%, `;
view_set := ""
Loop, % items0 - 1 {
If ( InStr(candidate_set, items%A_Index% ) > 0 )
view_set := view_set . items%A_Index% . ";"
}
view_var := "View_#" . view_m%A_Index% . "_#" . view_v%A_Index% . "_wndIds"
%view_var% := view_set
}
}
; No windows are known to the system yet. ; No windows are known to the system yet.
; Try to do something smart with the initial layout. ; Try to do something smart with the initial layout.
Manager_initial_sync() { Manager_initial_sync() {
@ -751,9 +995,14 @@ Manager_initial_sync() {
Loop, % Manager_monitorCount Loop, % Manager_monitorCount
Manager_initial_sync_m#%A_Index%_wndList := List_new() Manager_initial_sync_m#%A_Index%_wndList := List_new()
; check all visible windows against the known windows ; Use saved window placement settings to first determine
; which monitor/view a window should be attached to.
Manager__restoreWindowState(Main_autoWindowState)
; check all remaining visible windows against the known windows
WinGet, wndId, List, , , WinGet, wndId, List, , ,
Loop, % wndId { Loop, % wndId {
; Based on some analysis here, determine which monitors and layouts would best ; Based on some analysis here, determine which monitors and layouts would best
; serve existing windows. Do not override configuration settings. ; serve existing windows. Do not override configuration settings.
@ -769,8 +1018,8 @@ Manager_initial_sync() {
If m > 0 If m > 0
List_append(Manager_initial_sync_m#%m%_wndList, wndId%A_index%) List_append(Manager_initial_sync_m#%m%_wndList, wndId%A_index%)
; @todo: What percentage of the monitor area is it occupying? (Suggest layout) ; @todo-future: What percentage of the monitor area is it occupying? (Suggest layout)
; @todo: What part of the monitor is it on? (Ordering of windows) ; @todo-future: What part of the monitor is it on? (Ordering of windows)
} }
Loop, % Manager_monitorCount { Loop, % Manager_monitorCount {
@ -795,6 +1044,8 @@ Manager_toggleDecor() {
Manager_unmanage(wndId) { Manager_unmanage(wndId) {
Local a Local a
; Do our best to make sure that any unmanaged windows are left visible.
Manager_winShow(wndId)
a := Manager_#%wndId%_tags & 1 << Monitor_#%Manager_aMonitor%_aView_#1 - 1 a := Manager_#%wndId%_tags & 1 << Monitor_#%Manager_aMonitor%_aView_#1 - 1
Loop, % Config_viewCount Loop, % Config_viewCount
If (Manager_#%wndId%_tags & 1 << A_Index - 1) { If (Manager_#%wndId%_tags & 1 << A_Index - 1) {
@ -887,6 +1138,8 @@ Manager_winClose(wndId) {
Return 1 Return 1
} }
Else { Else {
; @todo: Identify the next active window, activate it, then close the window to
; be closed.
WinClose, ahk_id %wndId% WinClose, ahk_id %wndId%
Return 0 Return 0
} }
@ -908,7 +1161,7 @@ Manager_winSet(type, value, wndId) {
; 1 - Hung ; 1 - Hung
Manager_isHung(wndId) { Manager_isHung(wndId) {
Local result, detect_setting, WM_NULL Local result, detect_setting, WM_NULL
WM_NULL := 0 WM_NULL = 0
detect_setting := A_DetectHiddenWindows detect_setting := A_DetectHiddenWindows
DetectHiddenWindows, On DetectHiddenWindows, On
SendMessage, WM_NULL, , , , ahk_id %wndId% SendMessage, WM_NULL, , , , ahk_id %wndId%

View file

@ -26,7 +26,7 @@ Monitor_init(m) {
Monitor_#%m%_showBar := Config_showBar Monitor_#%m%_showBar := Config_showBar
Loop, % Config_viewCount Loop, % Config_viewCount
View_init(m, A_Index) View_init(m, A_Index)
Config_restore("Monitor", m) Config_restoreLayout(Main_autoLayout, m)
Monitor_getWorkArea(m) Monitor_getWorkArea(m)
Bar_init(m) Bar_init(m)
} }