Can declare a hotkey in Config.ini that exits the app

This commit is contained in:
Joshua Fuhs 2012-07-03 20:50:23 -04:00
commit 9449b1a93f
14 changed files with 1369 additions and 314 deletions

BIN
bugn.exe

Binary file not shown.

View file

@ -61,7 +61,7 @@ Bar_init(m) {
}
; layout
i := Config_viewCount + 1
text := " ??? "
text := " 1x9|=- "
w := Bar_getTextWidth(text)
Gui, Add, Text, x%x1% y%y1% w%w% h%h1% BackgroundTrans vBar_#%m%_#%i%_layout gBar_GuiClick,
Gui, Add, Progress, x%x1% y%y1% w%w% h%h1% Background%Config_normBgColor2%
@ -214,8 +214,10 @@ Bar_initCmdGui() {
TV_Add("rotate master axis", itemId20)
TV_Add("rotate stack axis", itemId20)
TV_Add("mirror tile layout", itemId20)
TV_Add("increase master split", itemId20)
TV_Add("decrease master split", itemId20)
TV_Add("increase master X", itemId20)
TV_Add("decrease master X", itemId20)
TV_Add("increase master Y", itemId20)
TV_Add("decrease master Y", itemId20)
TV_Add("increase master factor", itemId20)
TV_Add("decrease master factor", itemId20)
itemId30 := TV_Add("View")
@ -229,6 +231,12 @@ Bar_initCmdGui() {
TV_Add("toggle task bar", itemId40)
TV_Add("activate next", itemId40)
TV_Add("activate prev", itemId40)
;itemId50 := TV_add("Log")
; TV_Add("increment debug level", itemId50)
; TV_Add("decrement debug level", itemId50)
; TV_Add("log help info", itemId50)
; TV_Add("log view window info", itemId50)
; TV_Add("log managed window info", itemId50)
TV_Add("Reload")
TV_Add("Quit")
GuiControl, +Redraw, Bar_#0_#0
@ -320,10 +328,14 @@ Bar_evaluateCommand() {
View_rotateLayoutAxis(3, +1)
Else If (Bar_command_#1 = "mirror tile layout")
View_rotateLayoutAxis(1, +2)
Else If (Bar_command_#1 = "increase master split")
View_setMSplit(+1)
Else If (Bar_command_#1 = "decrease master split")
View_setMSplit(-1)
Else If (Bar_command_#1 = "increase master X")
View_setMX(+1)
Else If (Bar_command_#1 = "decrease master X")
View_setMX(-1)
Else If (Bar_command_#1 = "increase master Y")
View_setMY(+1)
Else If (Bar_command_#1 = "decrease master Y")
View_setMY(-1)
Else If (Bar_command_#1 = "increase master factor")
View_setMFactor(+0.05)
Else If (Bar_command_#1 = "decrease master factor")
@ -349,6 +361,17 @@ Bar_evaluateCommand() {
Manager_activateMonitor(+1)
Else If (Bar_command_#1 = "activate prev")
Manager_activateMonitor(-1)
} Else If (Bar_command_#2 = "Log") {
If (Bar_command_#1 = "increment debug level")
Log_incDebugLevel()
If (Bar_command_#1 = "decrement debug level")
Log_decDebugLevel()
If (Bar_command_#1 = "log help info")
Manager_logHelp()
If (Bar_command_#1 = "log view window info")
Manager_logViewWindowList()
If (Bar_command_#1 = "log managed window info")
Manager_logManagedWindowList()
} Else If (Bar_command_#1 = "Reload")
Main_reload()
Else If (Bar_command_#1 = "Quit")
@ -697,28 +720,34 @@ Bar_updateTitle(debugMsg = "") {
Bar_aWndId := aWndId
}
; Update the view portion of the status bar.
Bar_updateView(m, v) {
Local managedWndId0, wndId0, wndIds
Local IdsLen, ViewIdsLen
StringTrimRight, wndIds, Manager_managedWndIds, 1
StringSplit, managedWndId, wndIds, `;
GuiN := (m - 1) + 1
Gui, %GuiN%: Default
IdsLen := StrLen(Manager_managedWndIds)
If (v = Monitor_#%m%_aView_#1) {
; Set foreground/background colors if the view is the current view.
GuiControl, +Background%Config_selBgColor1% +c%Config_selFgColor2%, Bar_#%m%_#%v%_tagged
GuiControl, +c%Config_selFgColor1%, Bar_#%m%_#%v%
} Else If StrLen(View_#%m%_#%v%_wndIds) > 0 {
; Set foreground/background colors if the view contains windows.
GuiControl, +Background%Config_normBgColor5% +c%Config_normFgColor8%, Bar_#%m%_#%v%_tagged
GuiControl, +c%Config_normFgColor7%, Bar_#%m%_#%v%
} Else {
; Set foreground/background colors if the view is empty.
GuiControl, +Background%Config_normBgColor1% +c%Config_normFgColor8%, Bar_#%m%_#%v%_tagged
GuiControl, +c%Config_normFgColor1%, Bar_#%m%_#%v%
}
Loop, %Config_viewCount% {
StringTrimRight, wndIds, View_#%m%_#%A_Index%_wndIds, 1
StringSplit, wndId, wndIds, `;
If (A_Index = v)
If (v = Monitor_#%m%_aView_#1) {
GuiControl, +Background%Config_selBgColor1% +c%Config_selFgColor2%, Bar_#%m%_#%v%_tagged
GuiControl, +c%Config_selFgColor1%, Bar_#%m%_#%v%
} Else If wndId0 {
GuiControl, +Background%Config_normBgColor5% +c%Config_normFgColor8%, Bar_#%m%_#%v%_tagged
GuiControl, +c%Config_normFgColor7%, Bar_#%m%_#%v%
} Else {
GuiControl, +Background%Config_normBgColor1% +c%Config_normFgColor8%, Bar_#%m%_#%v%_tagged
GuiControl, +c%Config_normFgColor1%, Bar_#%m%_#%v%
}
GuiControl, , Bar_#%m%_#%A_Index%_tagged, % wndId0 / managedWndId0 * 100
ViewIdsLen := StrLen( View_#%m%_#%A_Index%_wndIds )
; Update the percentage fill for the view.
GuiControl, , Bar_#%m%_#%A_Index%_tagged, % ViewIdsLen / IdsLen * 100
; Refresh the number on the bar.
GuiControl, , Bar_#%m%_#%A_Index%, %A_Index%
}
}

View file

@ -23,11 +23,11 @@ Config_init() {
; status bar
Config_showBar := True ; If false, the bar is hidden. It can be made visible or hidden by hotkey (see below).
Config_horizontalBarPos := "left" ; The horizontal position of the bar: "center", "left" or "right" side of the monitor or an offset in pixel (px) from the left (>= 0) or right (< 0).
Config_verticalBarPos := "top" ; The vertical position of the bar: "top" or "bottom" of the monitor, "tray" = sub-window of the task bar.
Config_barWidth := "100%" ; The width of the bar in pixel (px) or with a per cent sign (%) as a percentage.
Config_singleRowBar := True ; If false, the bar will have to rows, one for the window title and one for all other GUI controls.
Config_spaciousBar := False ; If true, the height of the bar will be set to a value equal to the height of an edit control, else it will be set to the text height.
Config_horizontalBarPos := "left" ; The horizontal position of the bar: "center", "left" or "right" side of the monitor or an offset in pixel (px) from the left (>= 0) or right (< 0).
Config_verticalBarPos := "top" ; The vertical position of the bar: "top" or "bottom" of the monitor, "tray" = sub-window of the task bar.
Config_barWidth := "100%" ; The width of the bar in pixel (px) or with a per cent sign (%) as a percentage.
Config_singleRowBar := True ; If false, the bar will have to rows, one for the window title and one for all other GUI controls.
Config_spaciousBar := False ; If true, the height of the bar will be set to a value equal to the height of an edit control, else it will be set to the text height.
Config_fontName := "Lucida Console" ; A monospace font is preferable for bug.n to calculate the correct width of the bar and its elements (sub-windows).
Config_fontSize :=
Config_normBgColor :=
@ -37,7 +37,7 @@ Config_init() {
Config_readinBat := False ; If true, the system battery status is read in and displayed in the status bar. This only makes sense, if you have a system battery (notebook).
Config_readinCpu := False ; If true, the current CPU load is read in and displayed in the status bar.
Config_readinDate := True ; If true, the current date is read in (format: "WW, DD. MMM. YYYY") and displayed in the status bar.
Config_readinDiskLoad := False ; If true, the current disk load (read and write) is read in and displayed in the status bar.
Config_readinDiskLoad := False ; If true, the current disk load (read and write) is read in and displayed in the status bar.
Config_readinMemoryUsage := False ; If true, the system memory usage is read in and displayed in the status bar.
Config_readinNetworkLoad := False ; If true, the current network load (up and down) is read in and displayed in the status bar.
Config_readinTime := True ; If true, the current time is read in (format: "HH:MM") and displayed in the status bar.
@ -60,13 +60,13 @@ Config_init() {
Config_layoutAxis_#1 := 1 ; The layout axis: 1 = x, 2 = y; negative values mirror the layout, setting the master area to the right / bottom instead of left / top.
Config_layoutAxis_#2 := 2 ; The master axis: 1 = x (from left to right), 2 = y (from top to bottom), 3 = z (monocle).
Config_layoutAxis_#3 := 2 ; The stack axis: 1 = x (from left to right), 2 = y (from top to bottom), 3 = z (monocle).
Config_layoutGapWidth := 0 ; The default gap width in px (only even numbers) of the "tile" layout, i. e. the space between windows and around the layout.
Config_layoutGapWidth := 0 ; The default gap width in px (only even numbers) of the "tile" layout, i. e. the space between windows and around the layout.
Config_layoutMFactor := 0.6 ; The factor for the size of the master area, which is multiplied by the monitor size.
Config_mouseFollowsFocus := True ; If true, the mouse pointer is set over the focused window, if a window is activated by bug.n.
Config_onActiveHiddenWnds := "view" ; The action, which will be taken, if a window e. g. should be activated, but is not visible; "view": show the view accordng to the first tag of the window in question, "tag": add the window in question to the current visible view, "hide": hide the window again ignoring the activation.
Config_newWndPosition := "top" ; The position of a new window in a view; "top": at the beginning of the window list and the master area (default), "masterBottom": at the end of the master area, "stackTop": on top of the stack area, "bottom": at the end of the window list and the stack area.
Config_newWndPosition := "top" ; The position of a new window in a view; "top": at the beginning of the window list and the master area (default), "masterBottom": at the end of the master area, "stackTop": on top of the stack area, "bottom": at the end of the window list and the stack area.
Config_shellMsgDelay := 350 ; The time bug.n waits after a shell message (a window is opened, closed or the focus has been changed); if there are any problems recognizing, when windows are opened or closed, try to increase this number.
Config_syncMonitorViews := 0 ; The number of monitors (2 or more), for which views should be activated, when using the accordant hotkey. If set to 1, the views are actiated for all monitors. If set to 0, views are activated independently (only on the active monitor).
Config_syncMonitorViews := 0 ; The number of monitors (2 or more), for which views should be activated, when using the accordant hotkey. If set to 1, the views are activated for all monitors. If set to 0, views are activated independently (only on the active monitor).
Config_viewFollowsTagged := False ; If true and a window is tagged with a single tag, the view is correspondingly set to the tag.
; Config_rule_#i := "<class (regular expression string)>;<title (regular expression string)>;<window style (hexadecimal number or blank)>;
@ -363,8 +363,10 @@ Config_saveSession() {
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%_layoutMSplit = 1)
text .= "View_#" m "_#" A_Index "_layoutMSplit=" View_#%m%_#%A_Index%_layoutMSplit "`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"
}
}
@ -390,6 +392,11 @@ Config_saveSession() {
#+x::Manager_maximizeWindow() ; Move and resize the active window to the size of the work area (only floating windows).
#i::Manager_getWindowInfo() ; Get information for the active window (id, title, class, process name, style, geometry, tags and floating state).
#+i::Manager_getWindowList() ; Get a window list for the active view (id, title and class).
#^i::Manager_logViewWindowList() ; Dump window information for the active view.
#+^i::Manager_logManagedWindowList() ; Dump window information for every managed window.
#^h::Manager_logHelp() ; Dump to the log an explanation of some of the other more cryptic log messages.
#^[::Log_decDebugLevel() ; Decrement the log debug level.
#^]::Log_incDebugLevel() ; Increment the log debug level.
#Tab::View_setLayout(-1) ; Set the previously set layout. You may also use View_setLayout(">") for setting the next layout in the layout array.
#f::View_setLayout(3) ; Set the 3rd defined layout (i. e. floating layout in the default configuration).
@ -401,10 +408,13 @@ Config_saveSession() {
#^Enter::View_rotateLayoutAxis(1, +2) ; Mirror the layout axis (i. e. -1 -> 1 / 1 -> -1 = master on the left / right side, -2 -> 2 / 2 -> -2 = master at top / bottom, only for the "tile" layout).
#^Tab::View_rotateLayoutAxis(2, +1) ; Rotate the master axis (i. e. 3 -> 1 = x-axis = horizontal stack, 1 -> 2 = y-axis = vertical stack, 2 -> 3 = z-axis = monocle, only for the "tile" layout).
#^+Tab::View_rotateLayoutAxis(3, +1) ; Rotate the stack axis (i. e. 3 -> 1 = x-axis = horizontal stack, 1 -> 2 = y-axis = vertical stack, 2 -> 3 = z-axis = monocle, only for the "tile" layout).
#^Left::View_setMSplit(+1) ; Move the master splitter, i. e. decrease the number of windows in the master area (only for the "tile" layout).
#^Right::View_setMSplit(-1) ; Move the master splitter, i. e. increase the number of windows in the master area (only for the "tile" layout).
#<::View_setGapWidth(-2) ; Decrease the gap width by 2 px (only for the "tile" layout and even numbers; see the variable "Config_layoutGapWidth").
#+<::View_setGapWidth(+2) ; Increase the gap width by 2 px (only for the "tile" layout and even numbers; see the variable "Config_layoutGapWidth").
#^Up::View_setMY(+1) ; Increase the master Y dimension [1,9] (only for the "tile" layout).
#^Down::View_setMY(-1) ; Decrease the master Y dimension [1,9] (only for the "tile" layout).
#^Right::View_setMX(+1) ; Increase the master X dimension [1,9] (only for the "tile" layout).
#^Left::View_setMX(-1) ; Decrease the master X dimension [1,9] (only for the "tile" layout).
#+Left::View_setGapWidth(-2) ; Decrease the gap width by 2 px (only for the "tile" layout and even numbers; see the variable "Config_layoutGapWidth").
#+Right::View_setGapWidth(+2) ; Increase the gap width by 2 px (only for the "tile" layout and even numbers; see the variable "Config_layoutGapWidth").
#BackSpace::Monitor_activateView(-1) ; Activate the previously activated view. You may also use Monitor_activateView("<") or Monitor_activateView(">") for activating the previous or next adjacent view.
#+0::Monitor_setWindowTag(0) ; Tag the active window with all tags (1 ... Config_viewCount). You may also use Monitor_setWindowTag("<") or Monitor_setWindowTag(">") for setting the tag of the previous or next adjacent to the current view.

111
src/List.ahk Normal file
View file

@ -0,0 +1,111 @@
/**
* AHK List implementation
* Copyright (c) 2012 Joshua Fuhs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/**
* This is an admittedly poor list implementation, but I was new to AHK,
* and I had seen several examples of this already used.
* The lists being operated on should not be larger than a few hundred,
* and preferably no larger than one hundred.
*/
List_new() {
Global
Return ""
}
List_prepend( ByRef l, e ) {
l := e . "`;" . l
Return
}
List_append(ByRef l, e) {
l := l . e . "`;"
Return
}
; Insert at position immediately preceding index p
List_insert(ByRef l, e, p) {
Local arr, search, replace
If ( p = 0)
return List_prepend(l, e)
StringSplit arr, l, `;
if ( p >= arr0 - 1 )
return List_append(l, e)
p += 1
search := arr%p% . ";"
replace := e . ";" . search
StringReplace, l, l, %search%, %replace%
}
List_remove(ByRef l, e) {
Local search
search := "" . e . ";"
StringReplace, l, l, %search%,
return
}
List_removeAt(ByRef l, p) {
Local arr, search
StringSplit arr, l, `;
if( p >= arr0 - 1)
Return
p += 1
search := arr%p% . ";"
StringReplace, l, l, %search%,
}
List_find(ByRef l, e) {
Local arr, arr0
StringSplit arr, l, `;
Loop, % (arr0 - 1) {
If arr%A_Index% = %e%
Return (A_Index - 1)
}
Return -1
}
List_dump(l) {
Local result
StringReplace, result, l, `;, %A_Space%, All
Return result
}
List_get(l, p) {
Local arr
StringSplit arr, l, `;
If( p >= arr0 )
Return ""
p += 1
Return arr%p%
}
List_size(l) {
Local arr, arr0
StringSplit arr, l, `;
Return (arr0 - 1)
}
List_toArray(l, arrName) {
Local trimmedList
StringTrimRight, trimmedList, l, 1
StringSplit %arrName%, trimmedList, `;
Return (%arrName%0)
}

84
src/List.test.ahk Normal file
View file

@ -0,0 +1,84 @@
Test_check(Condition, msg) {
Global
If Condition
Return
Log_bare(msg)
Exit, 1
}
Log_init("List.test.txt", True)
testlist := List_new()
Log_bare("new list: " . List_dump(testlist))
Test_check(List_dump(testlist) = "", "Test 1 failure")
If Not (List_dump(testlist) = "" )
Log_bare("Test 1 failure")
List_append(testlist, "abc")
Log_bare("appended 'abc': " . List_dump(testlist))
If Not (List_dump(testlist) = "abc " )
Log_bare("Test 2 failure")
List_append(testlist, "def")
Log_bare("appended 'def': " . List_dump(testlist))
If Not (List_dump(testlist) = "abc def " )
Log_bare("Test 3 failure")
List_append(testlist, "ghi")
List_remove(testlist, "abc")
Log_bare("removed 'abc': " . List_dump(testlist))
If Not (List_dump(testlist) = "def ghi " )
Log_bare("Test 4 failure")
List_append(testlist, "jkl")
List_remove(testlist, "ghi")
Log_bare("add 'jkl', remove 'ghi': " . List_dump(testlist))
If Not (List_dump(testlist) = "def jkl " )
Log_bare("Test 5 failure")
List_append(testlist, "mno")
List_remove(testlist, "mno")
Log_bare("add and remove 'mno': " . List_dump(testlist))
If Not (List_dump(testlist) = "def jkl " )
Log_bare("Test 6 failure")
List_prepend(testlist, "12345")
Log_bare("prepend '12345': " . List_dump(testlist))
If Not (List_dump(testlist) = "12345 def jkl ")
Log_bare("Test 7 failure")
List_insert(testlist, "xyz", 0)
List_insert(testlist, "Happy", 1)
List_insert(testlist, "Blah", 5)
List_insert(testlist, "10", 10)
Log_bare("Attempt multiple inserts by index: " . List_dump(testlist))
If Not (List_dump(testlist) = "xyz Happy 12345 def jkl Blah 10 ")
Log_bare("Test 8 failure")
List_removeAt(testlist, 0)
List_removeAt(testlist, 1)
List_removeAt(testlist, 2)
List_removeAt(testlist, 3)
List_removeAt(testlist, 10)
Log_bare("Attempt multiple removals by index: " . List_dump(testlist))
If Not (List_dump(testlist) = "Happy def Blah ")
Log_bare("Test 9 failure")
If Not (List_find(testlist, "Happy") = 0)
Log_bare("Test 10 failure")
If Not (List_find(testlist, "def") = 1)
Log_bare("Test 11 failure")
If Not (List_find(testlist, "Blah") = 2)
Log_bare("Test 12 failure")
If Not (List_find(testlist, "nonexistent") = -1)
Log_bare("Test 13 failure")
If Not (List_size(testlist) = 3)
Log_bare("Test 14 failure")
If Not (List_get(testlist, 0) = "Happy")
Log_bare("Test 15 failure")
If Not (List_get(testlist, 1) = "def")
Log_bare("Test 16 failure")
If Not (List_get(testlist, 2) = "Blah")
Log_bare("Test 17 failure")
If Not (List_get(testlist, 3) = "")
Log_bare("Test 18 failure")
Return
#Include Log.ahk
#Include List.ahk

82
src/Log.ahk Executable file
View file

@ -0,0 +1,82 @@
/**
* AHK Debug log implementation
* Copyright (c) 2012 Joshua Fuhs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @version 8.3.0
*/
Log_debug_level := 0
Log_init(name, truncate) {
Global
If truncate
IfExist, %name%
FileDelete, %name%
Log_name := name
}
Log_msg( message ) {
Local CurrentTime
If Not Log_name
Return
FormatTime, CurrentTime, , yyyyMMddHHmmss
FileAppend, %CurrentTime% %message%`r`n, %Log_name%
}
Log_bare( message ) {
Local padded_message
If Not Log_name
Return
padded_message := " " . message . "`r`n"
FileAppend, %padded_message% , %Log_name%
}
Log_incDebugLevel() {
Global
If Not Log_name
Return
If ( Log_debug_level < 9 )
{
Log_debug_level += 1
Log_msg("Debug logging level incremented to " . Log_debug_level )
}
}
Log_decDebugLevel() {
Global
If Not Log_name
Return
If ( Log_debug_level > 0 ) {
Log_debug_level -= 1
If ( Log_debug_level = 0 )
Log_msg("Debug logging is disabled")
Else
Log_msg("Debug logging level decremented to " . Log_debug_level)
}
}
Log_dbg_msg( level, message ) {
Global
If (level > 0 And Log_debug_level >= level)
Log_msg( "DBG " . level . ": " . message )
}
Log_dbg_bare( level, message ) {
Global
If (level > 0 And Log_debug_level >= level)
Log_bare( "DBG " . level . ": " . message )
}

View file

@ -32,6 +32,23 @@ SetWinDelay, 10
#WinActivateForce
; pseudo main function
EnvGet, appDir, APPDATA
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 ======")
If 0 = 1
Config_filePath = %1%
Config_init()
@ -52,6 +69,7 @@ Return ; end of the auto-execute section
* function & label definitions
*/
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 ======")
If Config_autoSaveSession
Config_saveSession()
Manager_cleanup()
@ -129,6 +147,8 @@ Main_toggleBar:
Monitor_toggleBar()
Return
#Include Log.ahk
#Include List.ahk
#Include Bar.ahk
#Include Config.ahk
#Include Manager.ahk

View file

@ -57,7 +57,7 @@ Manager_init() {
Bar_hideTitleWndIds := ""
Manager_allWndIds := ""
Manager_managedWndIds := ""
Manager_sync()
Manager_initial_sync()
Bar_updateStatus()
Bar_updateTitle()
@ -125,12 +125,6 @@ Manager_applyRules(wndId, ByRef isManaged, ByRef m, ByRef tags, ByRef isFloating
hideTitle := rule9
}
}
If (m = 0)
m := Manager_aMonitor
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 << Monitor_#%m%_aView_#1 - 1
} Else {
isManaged := False
If wndTitle
@ -162,10 +156,10 @@ Manager_cleanup() {
Manager_hideShow := True
Loop, PARSE, wndIds, `;
{
WinShow, ahk_id %A_LoopField%
Manager_winShow(A_LoopField)
If Not Config_showBorder
WinSet, Style, +0x40000, ahk_id %A_LoopField%
WinSet, Style, +0xC00000, ahk_id %A_LoopField%
Manager_winSet("Style", "+0x40000", A_LoopField)
Manager_winSet("Style", "+0xC00000", A_LoopField)
}
; Show the task bar.
@ -182,8 +176,8 @@ Manager_cleanup() {
Loop, % Config_viewCount
View_arrange(m, A_Index)
}
WinSet, AlwaysOnTop, On, ahk_id %aWndId%
WinSet, AlwaysOnTop, Off, ahk_id %aWndId%
Manager_winSet("AlwaysOnTop", "On", aWndId)
Manager_winSet("AlwaysOnTop", "Off", aWndId)
DllCall("Shell32.dll\SHAppBarMessage", "UInt", (ABM_REMOVE := 0x1), "UInt", &Bar_appBarData)
; SKAN: Crazy Scripting : Quick Launcher for Portable Apps (http://www.autohotkey.com/forum/topic22398.html)
@ -194,7 +188,7 @@ Manager_closeWindow() {
WinGetClass, aWndClass, ahk_id %aWndId%
WinGetTitle, aWndTitle, ahk_id %aWndId%
If Not (aWndClass = "AutoHotkeyGUI" And RegExMatch(aWndTitle, "bug.n_BAR_[0-9]+"))
WinClose, ahk_id %aWndId%
Manager_winClose(aWndId)
}
Manager_getWindowInfo() {
@ -238,6 +232,112 @@ Manager_getWindowList() {
Clipboard := text
}
Manager_logViewLayout() {
}
Manager_logWindowInfo( w ) {
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
WinGet, wndId, ID, A
If wndId = %w%
isWinFocus := "*"
Else
isWinFocus := " "
v := Monitor_#%Manager_aMonitor%_aView_#1
If View_#%Manager_aMonitor%_#%v%_aWndId = %w%
isBugnActive := "*"
Else
isBugnActive := " "
WinGetTitle, wndTitle, ahk_id %w%
WinGetClass, wndClass, ahk_id %w%
WinGet, wndProc, ProcessName, ahk_id %w%
If InStr(Bar_hiddenWndIds, w)
isHidden := "*"
Else
isHidden := " "
If Manager_#%w%_isFloating
isFloating := "*"
Else
isFloating := " "
If Manager_#%w%_isDecorated
isDecorated := "*"
Else
isDecorated := " "
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_isHung(w)
isResponsive := " "
Else
isResponsive := "*"
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 G M`tTags`tX`tY`tW`tH`tStyle`t`tProc / Class / Title")
}
Manager_logViewWindowList() {
Local text, v, aWndId, wndIds, aWndTitle
v := Monitor_#%Manager_aMonitor%_aView_#1
Log_msg( "Window dump for active view (" . Manager_aMonitor . ", " . v . ")" )
Manager_logHeader()
StringTrimRight, wndIds, View_#%Manager_aMonitor%_#%v%_wndIds, 1
Loop, PARSE, wndIds, `;
{
Manager_logWindowInfo( A_LoopField )
}
}
Manager_logManagedWindowList() {
Local wndIds
Log_msg( "Window dump for manager" )
Manager_logHeader()
StringTrimRight, wndIds, Manager_managedWndIds, 1
Loop, PARSE, wndIds, `;
{
Manager_logWindowInfo( A_LoopField)
}
}
Manager_logHelp() {
Log_msg("Help Display")
Log_bare("Window list columns")
Log_bare(" ID - Windows ID. Unique, OS-assigned ID")
Log_bare(" H - Hidden. Whether bug.n thinks this window is hidden.")
Log_bare(" W - Windows active. This window is active according to Windows.")
Log_bare(" A - View active. This window is active according to bug.n.")
Log_bare(" F - Floating. This window should not be positioned and resized by the layout.")
Log_bare(" D - Decorated. Does the window have a title bar?")
Log_bare(" R - Responsive. Is responding to messages?")
Log_bare(" G - Ghost. Is this window a ghost of another hung window?")
Log_bare(" M - Monitor number.")
Log_bare(" Tags - Bit-mask of the views in which the window is active.")
Log_bare(" X - Windows X position.")
Log_bare(" Y - Windows Y position.")
Log_bare(" W - Windows width.")
Log_bare(" H - Windows height.")
Log_bare(" Style - Windows style.")
Log_bare(" Proc / Class / Title - Process/Class/Title of the window.")
}
Manager_lockWorkStation() {
Global Config_shellMsgDelay
@ -261,13 +361,50 @@ Manager_loop(index, increment, lowerBound, upperBound) {
Return, index
}
Manager_manage(wndId) {
Local a, c0, hideTitle, i, isDecorated, isFloating, isManaged, l, m, n, replace, search, tags
; Accept a window to be added to the system for management.
; Provide a monitor, view preference, but don't override the config.
; pm - Preferred monitor
; 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, 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)
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") {
@ -281,38 +418,26 @@ Manager_manage(wndId) {
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
If (Manager_#%wndId%_tags & 1 << A_Index - 1) {
l := View_#%m%_#%A_Index%_layout_#1
If (Config_layoutFunction_#%l% = "tile") And ((Config_newWndPosition = "masterBottom") Or (Config_newWndPosition = "stackTop")) {
n := View_getTiledWndIds(m, A_Index, wndIds)
If (n > 1) {
StringSplit, wndId, wndIds, `;
If (wndId0 < View_#%m%_#%A_Index%_layoutMSplit)
View_#%m%_#%A_Index%_layoutMSplit := wndId0
i := View_#%m%_#%A_Index%_layoutMSplit
search := wndId%i% ";"
replace := search wndId ";"
StringReplace, View_#%m%_#%A_Index%_wndIds, View_#%m%_#%A_Index%_wndIds, %search%, %replace%
} Else
View_#%m%_#%A_Index%_wndIds .= wndId ";"
If (Config_newWndPosition = "masterBottom") And (n > 0)
View_#%m%_#%A_Index%_layoutMSplit += 1
} Else If (Config_newWndPosition = "bottom")
View_#%m%_#%A_Index%_wndIds .= wndId ";"
Else
View_#%m%_#%A_Index%_wndIds := wndId ";" View_#%m%_#%A_Index%_wndIds
Bar_updateView(m, A_Index)
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
WinSet, Style, -0x40000, ahk_id %wndId%
Manager_winSet("Style", "-0x40000", wndId)
If Not Manager_#%wndId%_isDecorated
WinSet, Style, -0xC00000, ahk_id %wndId%
Manager_winSet("Style", "-0xC00000", wndId)
a := Manager_#%wndId%_tags & 1 << Monitor_#%m%_aView_#1 - 1
If a {
@ -320,7 +445,7 @@ Manager_manage(wndId) {
Manager_winActivate(wndId)
} Else {
Manager_hideShow := True
WinHide, ahk_id %wndId%
Manager_winHide(wndId)
Manager_hideShow := False
}
}
@ -341,7 +466,7 @@ Manager_maximizeWindow() {
l := View_#%Manager_aMonitor%_#%v%_layout_#1
If Not Manager_#%aWndId%_isFloating And Not (Config_layoutFunction_#%l% = "")
View_toggleFloating()
WinSet, Top,, ahk_id %aWndId%
Manager_winSet("Top", "", aWndId)
Manager_winMove(aWndId, Monitor_#%Manager_aMonitor%_x, Monitor_#%Manager_aMonitor%_y, Monitor_#%Manager_aMonitor%_width, Monitor_#%Manager_aMonitor%_height)
}
@ -354,19 +479,61 @@ Manager_moveWindow() {
l := View_#%Manager_aMonitor%_#%v%_layout_#1
If Not Manager_#%aWndId%_isFloating And Not (Config_layoutFunction_#%l% = "")
View_toggleFloating()
WinSet, Top,, ahk_id %aWndId%
Manager_winSet("Top", "", aWndId)
WM_SYSCOMMAND = 0x112
SC_MOVE = 0xF010
SendMessage, WM_SYSCOMMAND, SC_MOVE, , , ahk_id %aWndId%
}
HSHELL_WINDOWCREATED := 1
; Seems to get sent sometimes when windows are deactivated.
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, aWndClass, aWndHeight, aWndId, aWndTitle, aWndWidth, aWndX, aWndY, flag, 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
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%
@ -382,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) {
@ -393,42 +569,41 @@ Manager_onShellMessage(wParam, lParam) {
Sleep, %Config_shellMsgDelay%
}
If (wParam = 1 Or wParam = 6) And Not InStr(Manager_allWndIds, lParam ";") And Not InStr(Manager_managedWndIds, lParam ";")
a := Manager_manage(lParam)
Else {
flag := True
a := Manager_sync(wndIds)
If wndIds
a := False
}
If a {
isChanged := Manager_sync(wndIds)
If wndIds
isChanged := False
If a Or isChanged {
View_arrange(Manager_aMonitor, Monitor_#%Manager_aMonitor%_aView_#1)
Bar_updateView(Manager_aMonitor, Monitor_#%Manager_aMonitor%_aView_#1)
}
If flag
If (Manager_monitorCount > 1) {
WinGetPos, aWndX, aWndY, aWndWidth, aWndHeight, ahk_id %aWndId%
m := Monitor_get(aWndX + aWndWidth / 2, aWndY + aWndHeight / 2)
If m
Manager_aMonitor := m
}
If (Manager_monitorCount > 1) {
WinGetPos, aWndX, aWndY, aWndWidth, aWndHeight, ahk_id %aWndId%
m := Monitor_get(aWndX + aWndWidth / 2, aWndY + aWndHeight / 2)
If m
Manager_aMonitor := m
}
If wndIds {
If (Config_onActiveHiddenWnds = "view") {
wndId := SubStr(wndIds, 1, InStr(wndIds, ";") - 1)
Loop, % Config_viewCount
If (Manager_#%wndId%_tags & 1 << A_Index - 1) {
Log_dbg_msg(3, "Switching views because " . wndId . " is considered hidden and active")
Manager_aMonitor := Manager_#%wndId%_monitor
Monitor_activateView(A_Index)
Break
}
} Else {
StringTrimRight, wndIds, wndIds, 1
StringSplit, wndId, wndIds, `;
; Otherwise re-hide them.
If (Config_onActiveHiddenWnds = "hide") {
Loop, % wndId0
WinHide, % "ahk_id " wndId%A_Index%
Manager_winHide(wndId%A_Index%)
} Else If (Config_onActiveHiddenWnds = "tag") {
; Or tag all of them for the current view.
t := Monitor_#%Manager_aMonitor%_aView_#1
Loop, % wndId0 {
wndId := wndId%A_Index%
@ -521,7 +696,7 @@ Manager_sizeWindow() {
l := View_#%Manager_aMonitor%_#%v%_layout_#1
If Not Manager_#%aWndId%_isFloating And Not (Config_layoutFunction_#%l% = "")
View_toggleFloating()
WinSet, Top,, ahk_id %aWndId%
Manager_winSet("Top", "", aWndId)
WM_SYSCOMMAND = 0x112
SC_SIZE = 0xF000
@ -540,11 +715,14 @@ Manager_sync(ByRef wndIds = "") {
Loop, % wndId {
If Not InStr(shownWndIds, wndId%A_Index% ";") {
If Not InStr(Manager_managedWndIds, wndId%A_Index% ";") {
flag := Manager_manage(wndId%A_Index%)
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% ";"
}
@ -563,15 +741,55 @@ Manager_sync(ByRef wndIds = "") {
Return, a
}
; No windows are known to the system yet.
; Try to do something smart with the initial layout.
Manager_initial_sync() {
Local wndId0, wnd, wndX, wndY, wndW, wndH, x, y, m, len
; Initialize lists
; Note that these variables make this function non-reentrant.
Loop, % Manager_monitorCount
Manager_initial_sync_m#%A_Index%_wndList := List_new()
; check all visible windows against the known windows
WinGet, wndId, List, , ,
Loop, % wndId {
; Based on some analysis here, determine which monitors and layouts would best
; serve existing windows. Do not override configuration settings.
; Which monitor is it on?
wnd := wndId%A_Index%
WinGetPos, wndX, wndY, wndW, wndH, ahk_id %wnd%
x := wndX + wndW/2
y := wndY + wndH/2
m := Monitor_get(x, y)
If m > 0
List_append(Manager_initial_sync_m#%m%_wndList, wndId%A_index%)
; @todo: What percentage of the monitor area is it occupying? (Suggest layout)
; @todo: What part of the monitor is it on? (Ordering of windows)
}
Loop, % Manager_monitorCount {
m := A_Index
len := List_toArray(Manager_initial_sync_m#%m%_wndList, "Manager_initial_sync_tmpArray")
Loop, % len
Manager_manage(m, 1, Manager_initial_sync_tmpArray%A_Index%)
}
}
Manager_toggleDecor() {
Local aWndId
WinGet, aWndId, ID, A
Manager_#%aWndId%_isDecorated := Not Manager_#%aWndId%_isDecorated
If Manager_#%aWndId%_isDecorated
WinSet, Style, +0xC00000, ahk_id %aWndId%
Manager_winSet("Style", "+0xC00000", aWndId)
Else
WinSet, Style, -0xC00000, ahk_id %aWndId%
Manager_winSet("Style", "-0xC00000", aWndId)
}
Manager_unmanage(wndId) {
@ -580,9 +798,10 @@ Manager_unmanage(wndId) {
a := Manager_#%wndId%_tags & 1 << Monitor_#%Manager_aMonitor%_aView_#1 - 1
Loop, % Config_viewCount
If (Manager_#%wndId%_tags & 1 << A_Index - 1) {
StringReplace, View_#%Manager_aMonitor%_#%A_Index%_wndIds, View_#%Manager_aMonitor%_#%A_Index%_wndIds, %wndId%`;,
View_delWnd( Manager_aMonitor, A_Index, wndId )
Bar_updateView(Manager_aMonitor, A_Index)
}
Manager_#%wndId%_monitor :=
Manager_#%wndId%_tags :=
Manager_#%wndId%_isDecorated :=
Manager_#%wndId%_isFloating :=
@ -594,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 {
@ -603,15 +822,135 @@ 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))
}
WinActivate, ahk_id %wndId%
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) {
WinRestore, ahk_id %wndId%
If Manager_isHung(wndId) {
Log_dbg_msg(2, "Manager_winMove: Potentially hung window " . wndId)
Return 1
}
Else
WinRestore, ahk_id %wndId%
WM_ENTERSIZEMOVE = 0x0231
WM_EXITSIZEMOVE = 0x0232
SendMessage, WM_ENTERSIZEMOVE, , , , ahk_id %wndId%
WinMove, ahk_id %wndId%, , %x%, %y%, %width%, %height%
SendMessage, WM_EXITSIZEMOVE, , , , ahk_id %wndId%
If ErrorLevel {
Log_dbg_msg(2, "Manager_winMove: Potentially hung window " . wndId)
Return 1
}
Else {
WinMove, ahk_id %wndId%, , %x%, %y%, %width%, %height%
SendMessage, WM_EXITSIZEMOVE, , , , ahk_id %wndId%
}
}
Manager_winHide(wndId) {
If Manager_isHung(wndId) {
Log_dbg_msg(2, "Manager_winHide: Potentially hung window " . wndId)
Return 1
}
Else {
WinHide, ahk_id %wndId%
Return 0
}
}
Manager_winShow(wndId) {
If Manager_isHung(wndId) {
Log_dbg_msg(2, "Manager_winShow: Potentially hung window " . wndId)
Return 1
}
Else {
WinShow, ahk_id %wndId%
Return 0
}
}
Manager_winClose(wndId) {
If Manager_isHung(wndId) {
Log_dbg_msg(2, "Manager_winClose: Potentially hung window " . wndId)
Return 1
}
Else {
WinClose, ahk_id %wndId%
Return 0
}
}
Manager_winSet(type, value, wndId) {
If Manager_isHung(wndId) {
Log_dbg_msg(2, "Manager_winSet: Potentially hung window " . wndId)
Return 1
}
Else {
WinSet, %type%, %value%, ahk_id %wndId%
Return 0
}
}
; 0 - Not hung
; 1 - Hung
Manager_isHung(wndId) {
Local result, detect_setting, WM_NULL
WM_NULL := 0
detect_setting := A_DetectHiddenWindows
DetectHiddenWindows, On
SendMessage, WM_NULL, , , , ahk_id %wndId%
result := ErrorLevel
DetectHiddenWindows, %detect_setting%
If result
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
}

View file

@ -40,56 +40,71 @@ Monitor_activateView(v) {
v := Manager_loop(Monitor_#%Manager_aMonitor%_aView_#1, +1, 1, Config_viewCount)
Else If (v = "<")
v := Manager_loop(Monitor_#%Manager_aMonitor%_aView_#1, -1, 1, Config_viewCount)
If (v > 0) And (v <= Config_viewCount) And Not Manager_hideShow And Not (v = Monitor_#%Manager_aMonitor%_aView_#1) {
aView := Monitor_#%Manager_aMonitor%_aView_#1
WinGet, aWndId, ID, A
If WinExist("ahk_id" aWndId) And InStr(View_#%Manager_aMonitor%_#%aView%_wndIds, aWndId ";") {
WinGetClass, aWndClass, ahk_id %aWndId%
WinGetTitle, aWndTitle, ahk_id %aWndId%
If Not (aWndClass = "Progman") And Not (aWndClass = "AutoHotkeyGui" And SubStr(aWndTitle, 1, 10) = "bug.n_BAR_") And Not (aWndClass = "DesktopBackgroundClass")
View_#%Manager_aMonitor%_#%aView%_aWndId := aWndId
}
n := Config_syncMonitorViews
If (n = 1)
n := Manager_monitorCount
Else If (n < 1)
n := 1
Loop, % n {
If (n = 1)
m := Manager_aMonitor
Else
m := A_Index
Monitor_#%m%_aView_#2 := aView
Monitor_#%m%_aView_#1 := v
Manager_hideShow := True
StringTrimRight, wndIds, View_#%m%_#%aView%_wndIds, 1
Loop, PARSE, wndIds, `;
If Not (Manager_#%A_LoopField%_tags & (1 << v - 1))
WinHide, ahk_id %A_LoopField%
StringTrimRight, wndIds, View_#%m%_#%v%_wndIds, 1
Loop, PARSE, wndIds, `;
WinShow, ahk_id %A_LoopField%
Manager_hideShow := False
Bar_updateView(m, aView)
Bar_updateView(m, v)
View_arrange(m, v)
}
wndId := View_#%Manager_aMonitor%_#%v%_aWndId
If Not (wndId And WinExist("ahk_id" wndId)) {
If View_#%Manager_aMonitor%_#%v%_wndIds {
wndId := SubStr(View_#%Manager_aMonitor%_#%v%_wndIds, 1, InStr(View_#%Manager_aMonitor%_#%v%_wndIds, ";")-1)
View_#%Manager_aMonitor%_#%v%_aWndId := wndId
} Else
wndId := 0
}
Manager_winActivate(wndId)
Log_dbg_msg(1, "Monitor_activateView(" . v . ") Manager_aMonitor: " . Manager_aMonitor . "; wndIds: " . View_#%m%_#%aView%_wndIds)
If (v <= 0) Or (v > Config_viewCount) Or Manager_hideShow
Return
; Re-arrange the windows on the view.
If (v = Monitor_#%Manager_aMonitor%_aView_#1) {
View_arrange(Manager_aMonitor, v)
Return
}
aView := Monitor_#%Manager_aMonitor%_aView_#1
WinGet, aWndId, ID, A
If WinExist("ahk_id" aWndId) And InStr(View_#%Manager_aMonitor%_#%aView%_wndIds, aWndId ";") {
WinGetClass, aWndClass, ahk_id %aWndId%
WinGetTitle, aWndTitle, ahk_id %aWndId%
If Not (aWndClass = "Progman") And Not (aWndClass = "AutoHotkeyGui" And SubStr(aWndTitle, 1, 10) = "bug.n_BAR_") And Not (aWndClass = "DesktopBackgroundClass")
View_#%Manager_aMonitor%_#%aView%_aWndId := aWndId
}
n := 1
If (Config_syncMonitorViews > 0)
n := Manager_monitorCount
Loop, % n {
If (n = 1)
m := Manager_aMonitor
Else
m := A_Index
Monitor_#%m%_aView_#2 := aView
Monitor_#%m%_aView_#1 := v
Manager_hideShow := True
; Most of the operations here are dispersed to multiple _different_ windows.
; Delays in this part of the code are extremely noticeable and the users
; do a lot of view switching.
SetWinDelay, 0
StringTrimRight, wndIds, View_#%m%_#%aView%_wndIds, 1
Loop, PARSE, wndIds, `;
If Not (Manager_#%A_LoopField%_tags & (1 << v - 1))
Manager_winHide(A_LoopField)
SetWinDelay, 10
DetectHiddenWindows, On
View_arrange(m, v)
DetectHiddenWindows, Off
StringTrimRight, wndIds, View_#%m%_#%v%_wndIds, 1
SetWinDelay, 0
Loop, PARSE, wndIds, `;
Manager_winShow(A_LoopField)
SetWinDelay, 10
Manager_hideShow := False
Bar_updateView(m, aView)
Bar_updateView(m, v)
}
wndId := View_#%Manager_aMonitor%_#%v%_aWndId
If Not (wndId And WinExist("ahk_id" wndId)) {
If View_#%Manager_aMonitor%_#%v%_wndIds {
wndId := SubStr(View_#%Manager_aMonitor%_#%v%_wndIds, 1, InStr(View_#%Manager_aMonitor%_#%v%_wndIds, ";")-1)
View_#%Manager_aMonitor%_#%v%_aWndId := wndId
} Else
wndId := 0
}
Manager_winActivate(wndId)
}
Monitor_get(x, y) {
@ -164,19 +179,9 @@ Monitor_getWorkArea(m) {
}
Monitor_moveWindow(m, wndId) {
Local fX, fY, monitor, wndHeight, wndWidth, wndX, wndY
Global
WinGetPos, wndX, wndY, wndWidth, wndHeight, ahk_id %wndId%
monitor := Monitor_get(wndX+wndWidth/2, wndY+wndHeight/2)
If Not (m = monitor) {
; move the window to the target monitor and scale it, if it does not fit on the monitor
fX := Monitor_#%m%_width / Monitor_#%monitor%_width
fY := Monitor_#%m%_height / Monitor_#%monitor%_height
If (wndX-Monitor_#%monitor%_x+wndWidth > Monitor_#%m%_width) Or (wndY-Monitor_#%monitor%_y+wndHeight > Monitor_#%m%_height)
Manager_winMove(wndId, Monitor_#%m%_x+fX*(wndX-Monitor_#%monitor%_x), Monitor_#%m%_y+fY*(wndY-Monitor_#%monitor%_y), fX*wndWidth, fY*wndHeight)
Else
Manager_winMove(wndId, Monitor_#%m%_x+(wndX-Monitor_#%monitor%_x), Monitor_#%m%_y+(wndY-Monitor_#%monitor%_y), wndWidth, wndHeight)
}
Manager_#%wndId%_monitor = m
}
Monitor_setWindowTag(t) {
@ -220,7 +225,7 @@ Monitor_setWindowTag(t) {
Monitor_activateView(t)
Else {
Manager_hideShow := True
WinHide, ahk_id %aWndId%
Manager_winHide(aWndId)
Manager_hideShow := False
View_arrange(Manager_aMonitor, aView)
Bar_updateView(Manager_aMonitor, t)
@ -273,7 +278,7 @@ Monitor_toggleWindowTag(t) {
Bar_updateView(Manager_aMonitor, t)
If (t = Monitor_#%Manager_aMonitor%_aView_#1) {
Manager_hideShow := True
WinHide, ahk_id %aWndId%
Manager_winHide(aWndId)
Manager_hideShow := False
wndId := SubStr(View_#%Manager_aMonitor%_#%t%_wndIds, 1, InStr(View_#%Manager_aMonitor%_#%t%_wndIds, ";")-1)
Manager_winActivate(wndId)

View file

@ -29,44 +29,122 @@ View_init(m, v) {
View_#%m%_#%v%_layoutAxis_#3 := Config_layoutAxis_#3
View_#%m%_#%v%_layoutGapWidth := Config_layoutGapWidth
View_#%m%_#%v%_layoutMFact := Config_layoutMFactor
View_#%m%_#%v%_layoutMSplit := 1
View_#%m%_#%v%_layoutMX := 1
View_#%m%_#%v%_layoutMY := 1
View_#%m%_#%v%_layoutSymbol := Config_layoutSymbol_#1
View_#%m%_#%v%_wndIds := ""
}
View_activateWindow(d) {
Local aWndId, i, j, v, wndId, wndId0, wndIds
Local aWndId, i, j, v, wndId, wndId0, wndIds, failure, direction
Log_dbg_msg(1, "View_activateWindow(" . d . ")")
If (d = 0)
Return
WinGet, aWndId, ID, A
Log_dbg_bare(2, "Active Windows ID: " . aWndId)
v := Monitor_#%Manager_aMonitor%_aView_#1
Log_dbg_bare(2, "View (" . v . ") wndIds: " . View_#%Manager_aMonitor%_#%v%_wndIds)
StringTrimRight, wndIds, View_#%Manager_aMonitor%_#%v%_wndIds, 1
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
Break
}
If (d > 0)
direction = 1
Else
direction = -1
Log_dbg_bare(2, "Current wndId index: " . i)
j := Manager_loop(i, d, 1, wndId0)
wndId := wndId%j%
WinSet, AlwaysOnTop, On, ahk_id %wndId%
WinSet, AlwaysOnTop, Off, ahk_id %wndId%
If Manager_#%aWndId%_isFloating
WinSet, Bottom, , ahk_id %aWndId%
Manager_winActivate(wndId)
Loop, % wndId0 {
Log_dbg_bare(2, "Next wndId index: " . j)
wndId := wndId%j%
Manager_winSet("AlwaysOnTop", "On", wndId)
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)
}
}
}
View_updateLayout(m, v) {
Local fn, l, wndIds
l := View_#%m%_#%v%_layout_#1
fn := Config_layoutFunction_#%l%
View_updateLayout_%fn%(m, v)
}
; Add a window to the view in question.
View_addWnd(m, v, wndId) {
Local l, msplit, i, wndIds, n
l := View_#%m%_#%v%_layout_#1
If (Config_layoutFunction_#%l% = "tile") And ((Config_newWndPosition = "masterBottom") Or (Config_newWndPosition = "stackTop")) {
n := View_getTiledWndIds(m, v, wndIds)
msplit := View_#%m%_#%v%_layoutMX * View_#%m%_#%v%_layoutMY
If ( msplit = 1 And Config_newWndPosition="masterBottom" ) {
View_#%m%_#%v%_wndIds := wndId ";" . View_#%m%_#%v%_wndIds
}
Else If ( (Config_newWndPosition="masterBottom" And n < msplit) Or (Config_newWndPosition="stackTop" And n <= msplit) ) {
View_#%m%_#%v%_wndIds .= wndId ";"
}
Else {
If (Config_newWndPosition="masterBottom")
i := msplit - 1
Else
i := msplit
StringSplit, wndId, wndIds, `;
search := wndId%i% ";"
replace := search wndId ";"
StringReplace, View_#%m%_#%v%_wndIds, View_#%m%_#%v%_wndIds, %search%, %replace%
}
}
Else If (Config_newWndPosition = "bottom")
View_#%m%_#%v%_wndIds .= wndId ";"
Else
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%`;,
}
View_arrange(m, v) {
Local fn, l, wndIds
Log_dbg_msg(1, "View_arrange(" . m . ", " . v . ")")
; All window actions are performed on independent windows. A delay won't help.
SetWinDelay, 0
l := View_#%m%_#%v%_layout_#1
fn := Config_layoutFunction_#%l%
If fn And (View_getTiledWndIds(m, v, wndIds) Or fn = "tile")
View_%fn%(m, v, wndIds)
Else
View_#%m%_#%v%_layoutSymbol := Config_layoutSymbol_#%l%
View_getTiledWndIds(m, v, wndIds)
View_arrange_%fn%(m, v, wndIds)
View_updateLayout(m, v)
Bar_updateLayout(m)
SetWinDelay, 10
}
View_getTiledWndIds(m, v, ByRef tiledWndIds) {
@ -75,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 ";"
}
@ -84,18 +162,36 @@ View_getTiledWndIds(m, v, ByRef tiledWndIds) {
Return, n
}
View_monocle(m, v, wndIds) {
Local wndId0
StringTrimRight, wndIds, wndIds, 1
View_updateLayout_(m, v)
{
View_#%m%_#%v%_layoutSymbol := "><>"
}
View_arrange_(m, v)
{
; Place-holder
}
View_updateLayout_monocle(m, v)
{
Local wndIds, wndId, wndId0
StringTrimRight, wndIds, View_#%m%_#%v%_wndIds, 1
StringSplit, wndId, wndIds, `;
Loop, % wndId0
Manager_winMove(wndId%A_Index%, Monitor_#%m%_x, Monitor_#%m%_y, Monitor_#%m%_width, Monitor_#%m%_height)
View_#%m%_#%v%_layoutSymbol := "[" wndId0 "]"
}
View_arrange_monocle(m, v, wndIds) {
Local gw
gw := View_#%m%_#%v%_layoutGapWidth
StringTrimRight, wndIds, wndIds, 1
StringSplit, View_arrange_monocle_wndId, wndIds, `;
View_draw_stack("View_arrange_monocle_wndId", 1, View_arrange_monocle_wndId0, 0, Monitor_#%m%_x, Monitor_#%m%_y, Monitor_#%m%_width, Monitor_#%m%_height, gw/2)
}
View_rotateLayoutAxis(i, d) {
Local f, l, v
Local f, l, v, n, tmp
v := Monitor_#%Manager_aMonitor%_aView_#1
l := View_#%Manager_aMonitor%_#%v%_layout_#1
@ -107,8 +203,16 @@ View_rotateLayoutAxis(i, d) {
f := View_#%Manager_aMonitor%_#%v%_layoutAxis_#%i% / Abs(View_#%Manager_aMonitor%_#%v%_layoutAxis_#%i%)
View_#%Manager_aMonitor%_#%v%_layoutAxis_#%i% := f * Manager_loop(Abs(View_#%Manager_aMonitor%_#%v%_layoutAxis_#%i%), d, 1, 2)
}
} Else
View_#%Manager_aMonitor%_#%v%_layoutAxis_#%i% := Manager_loop(View_#%Manager_aMonitor%_#%v%_layoutAxis_#%i%, d, 1, 3)
} Else {
n := Manager_loop(View_#%Manager_aMonitor%_#%v%_layoutAxis_#%i%, d, 1, 3)
; When we rotate the axis, we may need to swap the X and Y dimensions.
If Not (n = View_#%Manager_aMonitor%_#%v%_layoutAxis_#%i%) And (n = 1) Or (View_#%Manager_aMonitor%_#%v%_layoutAxis_#%i% = 1) {
tmp := View_#%Manager_aMonitor%_#%v%_layoutMX
View_#%Manager_aMonitor%_#%v%_layoutMX := View_#%Manager_aMonitor%_#%v%_layoutMY
View_#%Manager_aMonitor%_#%v%_layoutMY := tmp
}
View_#%Manager_aMonitor%_#%v%_layoutAxis_#%i% := n
}
View_arrange(Manager_aMonitor, v)
}
}
@ -124,7 +228,7 @@ View_setGapWidth(d) {
Else
d := Ceil(d / 2) * 2
w := View_#%Manager_aMonitor%_#%v%_layoutGapWidth + d
If (w >= 0 And w < Monitor_#%Manager_aMonitor%_height And w < Monitor_#%Manager_aMonitor%_width) {
If (w < Monitor_#%Manager_aMonitor%_height And w < Monitor_#%Manager_aMonitor%_width) {
View_#%Manager_aMonitor%_#%v%_layoutGapWidth := w
View_arrange(Manager_aMonitor, v)
}
@ -154,11 +258,7 @@ View_setMFactor(d) {
v := Monitor_#%Manager_aMonitor%_aView_#1
l := View_#%Manager_aMonitor%_#%v%_layout_#1
If (Config_layoutFunction_#%l% = "tile") {
mfact := 0
If (d >= 1.05)
mfact := d
Else
mfact := View_#%Manager_aMonitor%_#%v%_layoutMFact + d
mfact := View_#%Manager_aMonitor%_#%v%_layoutMFact + d
If (mfact >= 0.05 And mfact <= 0.95) {
View_#%Manager_aMonitor%_#%v%_layoutMFact := mfact
View_arrange(Manager_aMonitor, v)
@ -166,15 +266,35 @@ View_setMFactor(d) {
}
}
View_setMSplit(d) {
Local l, n, v, wndIds
View_setMX(d) {
Local l, n, m, v
v := Monitor_#%Manager_aMonitor%_aView_#1
l := View_#%Manager_aMonitor%_#%v%_layout_#1
If (Config_layoutFunction_#%l% = "tile") {
n := View_getTiledWndIds(Manager_aMonitor, v, wndIds)
View_#%Manager_aMonitor%_#%v%_layoutMSplit := Manager_loop(View_#%Manager_aMonitor%_#%v%_layoutMSplit, d, 1, n)
View_arrange(Manager_aMonitor, v)
m := Manager_aMonitor
v := Monitor_#%m%_aView_#1
l := View_#%m%_#%v%_layout_#1
If Not (Config_layoutFunction_#%l% = "tile")
Return
n := View_#%m%_#%v%_layoutMX + d
If (n >= 1) And (n <= 9) {
View_#%m%_#%v%_layoutMX := n
View_arrange(m, v)
}
}
View_setMY(d) {
Local l, n, m, v
m := Manager_aMonitor
v := Monitor_#%m%_aView_#1
l := View_#%m%_#%v%_layout_#1
If Not (Config_layoutFunction_#%l% = "tile")
Return
n := View_#%m%_#%v%_layoutMY + d
If (n >= 1) And (n <= 9) {
View_#%m%_#%v%_layoutMY := n
View_arrange(m, v)
}
}
@ -219,117 +339,241 @@ View_shuffleWindow(d) {
}
}
View_tile(m, v, wndIds) {
Local axis1, axis2, axis3, gapW, h1, h2, i, mfact, msplit, n1, n2, sym1, sym3, w1, w2, wndId0, x1, x2, y1, y2
View_updateLayout_tile(m, v) {
Local axis1, axis2, axis3, mp, ms, sym1, sym3, master_div, master_dim, master_sym, stack_sym
; Main axis
; 1 - vertical divider, master left
; 2 - horizontal divider, master top
; -1 - vertical divider, master right
; -2 - horizontal divider, master bottom
axis1 := View_#%m%_#%v%_layoutAxis_#1
; Master axis
; 1 - vertical divider
; 2 - horizontal divider
; 3 - monocle
axis2 := View_#%m%_#%v%_layoutAxis_#2
; Stack axis
; 1 - vertical divider
; 2 - horizontal divider
; 3 - monocle
axis3 := View_#%m%_#%v%_layoutAxis_#3
gapW := View_#%m%_#%v%_layoutGapWidth
mfact := View_#%m%_#%v%_layoutMFact
msplit := View_#%m%_#%v%_layoutMSplit
mx := View_#%m%_#%v%_layoutMX
my := View_#%m%_#%v%_layoutMY
If ( Abs(axis1) = 1 )
master_div := "|"
Else
master_div := "="
If ( axis2 = 1 ) {
master_sym := "|"
master_dim := mx . "x" . my
}
Else If ( axis2 = 2 ) {
master_sym := "-"
master_dim := mx . "x" . my
}
Else
master_sym := "[" . (mx * my) . "]"
If ( axis3 = 1 )
stack_sym := "|"
Else If ( axis3 = 2 )
stack_sym := "-"
Else
stack_sym := "o"
If ( axis1 > 0 )
View_#%m%_#%v%_layoutSymbol := master_dim . master_sym . master_div . stack_sym
Else
View_#%m%_#%v%_layoutSymbol := stack_sym . master_div . master_sym . master_dim
}
; Stack a bunch of windows on top of each other.
;
; arrName - Name of a globally stored array of windows:
; %arrName%1, %arrName%2, ...
; off - Offset into the array from which to start drawing.
; len - Number of windows from the array to draw.
; dir - Determines the direction through which we traverse arrName
; x - View x-position
; y - View y-position
; w - View width
; h - View height
; margin - Number of pixels to put between the windows.
View_draw_stack( arrName, off, len, dir, x, y, w, h, margin ) {
Local base, inc
If (dir = 0) {
base := off
inc := 1
}
Else {
base := off + len - 1
inc := -1
}
x += margin
y += margin
w -= 2 * margin
h -= 2 * margin
Loop, % len {
Manager_winMove(%arrName%%base%, x, y, w, h)
base += inc
}
}
; Draw a row of windows.
;
; arrName - Name of a globally stored array of windows:
; %arrName%1, %arrName%2, ...
; off - Offset into the array from which to start drawing.
; len - Number of windows from the array to draw.
; dir - Determines the direction through which we traverse arrName
; axis - X/Y <=> 0/1
; x - View x-position
; y - View y-position
; w - View width
; h - View height
; margin - Number of pixels to put between the windows.
View_draw_row( arrName, off, len, dir, axis, x, y, w, h, margin ) {
Local base, inc, x_inc, y_inc, wHeight, wWidth
;Log_bare("View_draw_row(" . arrName . ", " . off . ", " . len . ", " . dir . ", " . axis . ", " . x . ", " . y . ", " . w . ", " . h . ", " . margin . ")")
If (dir = 0) {
; Left-to-right and top-to-bottom, depending on axis
base := off
inc := 1
}
Else {
; Right-to-left and bottom-to-top, depending on axis
base := off + len - 1
inc := -1
}
If (axis = 0) {
; Create row along X
x_inc := w / len
y_inc := 0
wWidth := x_inc - 2 * margin
wHeight := h - 2 * margin
}
Else {
; Create row along Y
x_inc := 0
y_inc := h / len
wWidth := w - 2 * margin
wHeight := y_inc - 2 * margin
}
; Set original positions with respect to the margins.
x += margin
y += margin
Loop, % len {
Manager_winMove(%arrName%%base%, x, y, wWidth, wHeight)
x += x_inc
y += y_inc
base += inc
}
}
View_arrange_tile_action(arrName, off, len, bugn_axis, x, y, w, h, m) {
; 161 is a magic number determined somewhere. Maybe make this configurable.
; Same with 2*Bar_height.
If (bugn_axis = 3 Or (bugn_axis = 1 And w/len < 161) Or (bugn_axis = 2 And h/len < (2*Bar_height)))
View_draw_stack(arrName, off, len, 0, x, y, w, h, m)
Else
View_draw_row(arrName, off, len, 0, bugn_axis - 1, x, y, w, h, m)
}
View_split_region(axis, split_point, x, y, w, h, ByRef x1, ByRef y1, ByRef w1, ByRef h1, ByRef x2, ByRef y2, ByRef w2, ByRef h2) {
x1 := x
y1 := y
If(axis = 0) {
w1 := w * split_point
w2 := w - w1
h1 := h
h2 := h
x2 := x + w1
y2 := y
}
Else
{
w1 := w
w2 := w
h1 := h * split_point
h2 := h - h1
x2 := x
y2 := y + h1
}
}
View_arrange_tile(m, v, wndIds) {
Local axis1, axis2, axis3, gapW_2, h1, h2, i, mfact, mp, ms, mx2, my2, mw2, mh2, msplit, n1, n2, w1, w2, x1, x2, y1, y2, flipped, stack_len, secondary_areas, areas_remaining, draw_windows
StringTrimRight, wndIds, wndIds, 1
StringSplit, wndId, wndIds, `;
If (msplit > wndId0) {
If (wndId0 < 1)
View_#%m%_#%v%_layoutMSplit := 1
Else
View_#%m%_#%v%_layoutMSplit := wndId0
msplit := View_#%m%_#%v%_layoutMSplit
StringSplit, View_arrange_tile_wndId, wndIds, `;
Log_dbg_msg(1, "View_arrange_tile: (" . View_arrange_tile_wndId0 . ") " . wndIds)
If (View_arrange_tile_wndId0 = 0)
Return
axis1 := Abs(View_#%m%_#%v%_layoutAxis_#1)
axis2 := View_#%m%_#%v%_layoutAxis_#2
axis3 := View_#%m%_#%v%_layoutAxis_#3
flipped := View_#%m%_#%v%_layoutAxis_#1 < 0
gapW_2 := View_#%m%_#%v%_layoutGapWidth/2
mfact := View_#%m%_#%v%_layoutMFact
dimAligned := (axis2 = 1) ? View_#%m%_#%v%_layoutMX : View_#%m%_#%v%_layoutMY
dimOrtho := (axis2 = 1) ? View_#%m%_#%v%_layoutMY : View_#%m%_#%v%_layoutMX
msplit := dimAligned * dimOrtho
If (msplit > View_arrange_tile_wndId0) {
msplit := View_arrange_tile_wndId0
}
; layout symbol
sym1 := "="
If (axis2 = Abs(axis1))
sym1 := "|"
If (axis2 = 3)
If (wndId0 = 0)
sym1 := 0
; master and stack area
If( View_arrange_tile_wndId0 > msplit) {
If( flipped = 0)
View_split_region( axis1 - 1, mfact, Monitor_#%m%_x, Monitor_#%m%_y, Monitor_#%m%_width, Monitor_#%m%_height, x1, y1, w1, h1, x2, y2, w2, h2)
Else
sym1 := msplit
sym3 := "="
If (axis3 = Abs(axis1))
sym3 := "|"
If (axis3 = 3)
If (wndId0 = 0)
sym3 := 0
Else
sym3 := wndId0 - msplit
If (axis1 < 0)
If (msplit = 1)
View_#%m%_#%v%_layoutSymbol := sym3 "[]"
Else
View_#%m%_#%v%_layoutSymbol := sym3 "[" sym1
View_split_region( axis1 - 1, 1 - mfact, Monitor_#%m%_x, Monitor_#%m%_y, Monitor_#%m%_width, Monitor_#%m%_height, x2, y2, w2, h2, x1, y1, w1, h1)
}
Else {
x1 := Monitor_#%m%_x
y1 := Monitor_#%m%_y
w1 := Monitor_#%m%_width
h1 := Monitor_#%m%_height
}
; master
; Number
If( axis2 = 3 )
{
View_draw_stack("View_arrange_tile_wndId", 1, msplit, 0, x1, y1, w1, h1, gapW_2)
}
Else
If (msplit = 1)
View_#%m%_#%v%_layoutSymbol := "[]" sym3
Else
View_#%m%_#%v%_layoutSymbol := sym1 "]" sym3
If (wndId0 > 0) {
; master and stack area
h1 := Monitor_#%m%_height - gapW
h2 := Monitor_#%m%_height - gapW
w1 := Monitor_#%m%_width - gapW
w2 := Monitor_#%m%_width - gapW
x1 := Monitor_#%m%_x + gapW / 2
x2 := Monitor_#%m%_x + gapW / 2
y1 := Monitor_#%m%_y + gapW / 2
y2 := Monitor_#%m%_y + gapW / 2
If (Abs(axis1) = 1 And wndId0 > msplit) {
w1 *= mfact
w2 -= w1
If (axis1 < 0)
x1 += w2
Else
x2 += w1
} Else If (Abs(axis1) = 2 And wndId0 > msplit) {
h1 *= mfact
h2 -= h1
If (axis1 < 0)
y1 += h2
Else
y2 += h1
}
; master
If (axis2 != 1 Or w1 / msplit < 161)
n1 := 1
Else
n1 := msplit
If (axis2 != 2 Or h1 / msplit < Bar_height)
n2 := 1
Else
n2 := msplit
Loop, % msplit {
Manager_winMove(wndId%A_Index%, x1 + gapW / 2, y1 + gapW / 2, w1 / n1 - gapW, h1 / n2 - gapW)
If (n1 > 1)
x1 += w1 / n1
If (n2 > 1)
y1 += h1 / n2
}
; stack
If (wndId0 > msplit) {
If (axis3 != 1 Or w2 / (wndId0 - msplit) < 161)
n1 := 1
Else
n1 := wndId0 - msplit
If (axis3 != 2 Or h2 / (wndId0 - msplit) < Bar_height)
n2 := 1
Else
n2 := wndId0 - msplit
Loop, % wndId0 - msplit {
i := msplit + A_Index
Manager_winMove(wndId%i%, x2 + gapW / 2, y2 + gapW / 2, w2 / n1 - gapW, h2 / n2 - gapW)
If (n1 > 1)
x2 += w2 / n1
If (n2 > 1)
y2 += h2 / n2
{
secondary_areas := Ceil(msplit / dimAligned)
areas_remaining := secondary_areas
windows_remaining := msplit
;Log_bare("msplit: " . msplit . "; layoutMX/Y: " . dimAligned . "; secondary_areas: " . secondary_areas . "; areas_remaining: " . areas_remaining . "; windows_remaining: " . windows_remaining)
Loop, % secondary_areas {
View_split_region(Not (axis2 - 1), (1/areas_remaining), x1, y1, w1, h1, mx1, my1, mw1, mh1, x1, y1, w1, h1)
draw_windows := dimAligned
If (windows_remaining < dimAligned) {
draw_windows := windows_remaining
}
View_draw_row("View_arrange_tile_wndId", msplit - windows_remaining + 1, draw_windows, 0, axis2 - 1, mx1, my1, mw1, mh1, gapW_2)
windows_remaining -= draw_windows
areas_remaining -= 1
}
}
; stack
If (View_arrange_tile_wndId0 <= msplit)
Return
stack_len := View_arrange_tile_wndId0 - msplit
View_arrange_tile_action("View_arrange_tile_wndId", msplit + 1, stack_len, axis3, x2, y2, w2, h2, gapW_2)
}
View_toggleFloating() {

View file

@ -10,6 +10,23 @@
(~) changed
(+) added
=8.2.2(?)=
(+) Multi-dimensional tiling of the master area. The user may now specify
X and Y dimensions independently up to 9 x 9.
(+) Initially assign windows to the monitor on which they appear if
nothing is specified in the configuration.
(~) Improved view arranging runtime.
(+) Created bug.n log to record major and debugging events.
(~) Fixed bug #18641: Freezing problem. In most cases, bug.n will no
longer hang when one of its managed windows hangs.
(~) Fixed bug: Sometimes cycling through the windows in a view would
get stuck on a particular window.
(~) Fixed bug: Activating a window on a non-active view on
a non-active monitor could cause both monitors to change views.
(~) Gap widths are now treated identically on both "tile" and "monocle"
layouts.
=8.2.1=

View file

@ -71,7 +71,7 @@ There are three layouts.
A master area for the main window(s) and a stacking area for the rest,
all windows are shown at any time. This layout can be further changed
in the following respects:
- the number of windows in the master area (1 ... all)
- the dimensions of the master area (1x1 ... 9x9)
- the stacking direction of the master and stacking area (from left to
right, from top to bottom or monocle)
- the position of the master area (left, top, right or bottom)

View file

@ -134,13 +134,21 @@ pressing the left Windows key and the shift key and the q key
1 -> 2 = y-axis = vertical stack, 2 -> 3 = z-axis = monocle, only for
the "tile" layout).
: #^Left::**View_setMSplit(+1)**
Move the master splitter, i. e. decrease the number of windows in the
master area (only for the "tile" layout).
: #^Left::**View_setMX(-1)**
Decrease the master X dimension by 1, i. e. decrease the number of
windows in the master area by Y. Minimum of 1.
: #^Right::**View_setMSplit(-1)**
Move the master splitter, i. e. increase the number of windows in the
master area (only for the "tile" layout).
: #^Right::**View_setMX(+1)
Increase the master X dimension by 1, i. e. increase the number of
windows in the master area by Y. Maximum of 9.
: #^Down::**View_setMY(-1)**
Decrease the master Y dimension by 1, i.e. decrease the number of
windows in the master area by X. Minimum of 1.
: #^Up::**View_setMY(+1)**
Increase the master Y dimension by 1, i.e. increase the number of
windows in the master area by X. Maximum of 9.
=== Tag / View related hotkeys ===
@ -197,6 +205,25 @@ pressing the left Windows key and the shift key and the q key
Hide / Show the task bar.
=== Logging/Debugging related hotkeys ===
: #^i::**Manager_logViewWindowList()**
Dump the contents of the current view to the bug.n log.
: #+^i::**Manager_logManagedWindowList()**
Dump the contents of the managed window list to the bug.n log.
: #^h::**Manager_logHelp()**
Print to the log a description of the formatting used in the
previous two log messages.
: #^[::**Log_decDebugLevel()**
Decrement the debug log level. Show fewer debug messages.
: #^]::**Log_incDebugLevel()**
Increment the debug log level. Show more debug messages.
=== Application related hotkeys ===
: #y::**Bar_toggleCommandGui()**

87
test/hang.cpp Normal file
View file

@ -0,0 +1,87 @@
/**
* Intended to be built with MinGW.
*
*/
#include <windows.h>
char *AppTitle="Win1";
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
int WinMain(HINSTANCE hInst,HINSTANCE,LPSTR,int nCmdShow)
{
WNDCLASS wc;
HWND hwnd;
MSG msg;
wc.style=CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc=WindowProc;
wc.cbClsExtra=0;
wc.cbWndExtra=0;
wc.hInstance=hInst;
wc.hIcon=LoadIcon(NULL,IDI_WINLOGO);
wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground=(HBRUSH)COLOR_WINDOWFRAME;
wc.lpszMenuName=NULL;
wc.lpszClassName=AppTitle;
if (!RegisterClass(&wc))
return 0;
hwnd = CreateWindow(AppTitle,AppTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,100,100,
NULL,NULL,hInst,NULL);
if (!hwnd)
return 0;
ShowWindow(hwnd,nCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg,NULL,0,0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
int maintain_bomb = 60;
int activate_bomb = 5;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC dc;
RECT r;
GetClientRect(hwnd,&r);
dc=BeginPaint(hwnd,&ps);
DrawText(dc,"This window intentionally hangs",-1,&r,DT_SINGLELINE|DT_CENTER|DT_VCENTER);
EndPaint(hwnd,&ps);
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_SHOWWINDOW:
case WM_ACTIVATE:
if(activate_bomb == 0)
while(maintain_bomb) {
Sleep(1000);
maintain_bomb--;
}
activate_bomb--;
default:
return DefWindowProc(hwnd, msg, wparam, lparam);
}
return 0;
}