ahk/Moonlander Window Manager.ahk
Fernando Schauenburg fd356cdc61 Replace Firefox with Librewolf
I had to switch to using `ahk_exe` for identifying the windows rather
than `ahk_class` because Librewolf's windows' class is
`MozillaWindowClass`, just like Firefox. That means that, if there was
already a Firefox window open, my shortcut would activate that window
instead of launching Librewolf.
2025-03-14 15:12:55 +01:00

191 lines
6.1 KiB
AutoHotkey

; See LICENSE file for copyright and license details.
;
; Simple window manager for MS Windows, made to work in tandem with my
; Moonlander keyboard configuration:
;
; https://configure.zsa.io/moonlander/layouts/553D6/latest/0
try TraySetIcon("img\win_manager.ico")
TERMINAL_EXE := "ahk_exe mintty.exe"
TERMINAL_CMD := "
(LTrim Join`s
C:\Users\fernando.schauenburg\AppData\Local\wsltty\bin\mintty.exe
--WSL=
--configdir="C:\Users\fernando.schauenburg\AppData\Roaming\wsltty"
-~
-
)"
BROWSER_EXE := "ahk_exe librewolf.exe"
BROWSER_CMD := "C:\Program Files\LibreWolf\librewolf.exe"
; Returns the rectangle (position & size) of a given monitor.
MonitorGetRect(N)
{
MonitorGetWorkArea(N, &left, &top, &right, &bottom)
return {
pos: {
x: left,
y: top,
},
size: {
width: Abs(right - left),
height: Abs(bottom - top),
}
}
}
; Return the number of the monitor that contains a window.
WinGetMonitor(WinTitle:="A")
{
WinGetPos(&x,, &width,, WinTitle)
midx := x + width // 2
Loop MonitorGetCount() {
MonitorGetWorkArea(A_Index, &left,, &right)
if (left <= midx and midx < right)
return A_Index
}
return MonitorGetPrimary() ; fallback to primary if none found
}
; Center a window without changing its size.
WinCenter(WinTitle:="A")
{
WinGetPos(,, &width, &height, WinTitle)
r := MonitorGetRect(WinGetMonitor(WinTitle))
x := Round(r.pos.x + r.size.width/2 - width/2)
y := Round(r.pos.y + r.size.height/2 - height/2)
WinMove(x, y, width, height, WinTitle)
}
; Return the rect (in percentages) of a window in its current monitor.
WinGetRelativeRect(WinTitle:="A")
{
WinGetPos(&x, &y, &width, &height, WinTitle)
monitor := WinGetMonitor(WinTitle)
rect := MonitorGetRect(monitor)
return {
pos: {
x: Abs((x - rect.pos.x) / rect.size.width),
y: Abs((y - rect.pos.y) / rect.size.height),
},
size: {
width: width / rect.size.width,
height: height / rect.size.height,
}
}
}
; Move and resize window to a rect (in percentage) in a given monitor.
WinSetRelativeRect(winRect, monitor?, WinTitle:="A")
{
if not IsSet(monitor)
monitor := WinGetMonitor(WinTitle)
rect := MonitorGetRect(monitor)
x := Round(rect.pos.x + winRect.pos.x * rect.size.width)
y := Round(rect.pos.y + winRect.pos.y * rect.size.height)
width := Round( winRect.size.width * rect.size.width)
height := Round( winRect.size.height * rect.size.height)
WinMove(x, y, width, height, WinTitle)
}
; Move a window wihtout changing its size.
WinTranslate(dx, dy, WinTitle:="A")
{
WinGetPos(&x, &y, &width, &height, WinTitle)
WinMove(x + dx, y + dy, width, height, WinTitle)
}
; Move a window to a different monitor, ajusting the relative scaling.
WinSetMonitor(target, WinTitle:="A")
{
WinSetRelativeRect(WinGetRelativeRect(WinTitle), target, WinTitle)
}
openProgram(WinExe, Target, WorkingDir, rect?)
{
if WinExist(WinExe) {
WinActivate(WinExe)
} else {
Run(Target, WorkingDir)
if IsSet(rect)
if WinWait(WinExe,,5)
WinSetRelativeRect(rect, 1, WinExe)
}
}
POS := {
; 2x3 matrix
upperLeft : {pos: {x: 0, y: 0}, size: {width: 1/3, height: 1/2 }},
upperMiddle : {pos: {x: 1/3, y: 0}, size: {width: 1/3, height: 1/2 }},
upperRight : {pos: {x: 2/3, y: 0}, size: {width: 1/3, height: 1/2 }},
lowerLeft : {pos: {x: 0, y: 1/2}, size: {width: 1/3, height: 1/2 }},
lowerMiddle : {pos: {x: 1/3, y: 1/2}, size: {width: 1/3, height: 1/2 }},
lowerRight : {pos: {x: 2/3, y: 1/2}, size: {width: 1/3, height: 1/2 }},
; Full Height Thirds
thirdLeft : {pos: {x: 0, y: 0}, size: {width: 1/3, height: 1 }},
doubleLeft : {pos: {x: 0, y: 0}, size: {width: 2/3, height: 1 }},
thirdRight : {pos: {x: 2/3, y: 0}, size: {width: 1/3, height: 1 }},
doubleRight : {pos: {x: 1/3, y: 0}, size: {width: 2/3, height: 1 }},
; Full Height Halves
halfLeft : {pos: {x: 0, y: 0}, size: {width: 1/2, height: 1 }},
halfRight : {pos: {x: 1/2, y: 0}, size: {width: 1/2, height: 1 }},
; Center
mainFocus : {pos: {x: 0.18, y: 0}, size: {width: 0.64, height: 1 }},
fullScreen : {pos: {x: 0, y: 0}, size: {width: 1, height: 1 }},
}
; Uncomment the following line while making changes for easy reload.
; ^!r::Reload
; Make sure NumLock is active so that Numpad mappinfs below will work.
SetNumLockState True
; 2x3 matrix
!#Numpad7:: WinSetRelativeRect(POS.upperLeft)
!#NumpadDiv:: WinSetRelativeRect(POS.upperMiddle)
!#Numpad8:: WinSetRelativeRect(POS.upperMiddle)
!#Numpad9:: WinSetRelativeRect(POS.upperRight)
!#Numpad1:: WinSetRelativeRect(POS.lowerLeft)
!#NumpadSub:: WinSetRelativeRect(POS.lowerMiddle)
!#Numpad2:: WinSetRelativeRect(POS.lowerMiddle)
!#Numpad3:: WinSetRelativeRect(POS.lowerRight)
; Full height thirds
!#Numpad4:: WinSetRelativeRect(POS.thirdLeft)
!#NumpadMult:: WinSetRelativeRect(POS.doubleLeft)
!#Numpad5:: WinSetRelativeRect(POS.doubleRight)
!#Numpad6:: WinSetRelativeRect(POS.thirdRight)
; Full height halves
!^Numpad1:: WinSetRelativeRect(POS.halfLeft)
!^Numpad3:: WinSetRelativeRect(POS.halfRight)
; Center and...
!#NumpadAdd:: WinSetRelativeRect(POS.fullScreen) ; ... make full screen.
!^Numpad5:: WinCenter() ; ... keep size.
; Move to other monitor (FIXME this is a hack, but works on my current setup)
!^Left:: WinSetMonitor(2)
!^Right:: WinSetMonitor(1)
; Move without resize
step := 50 ; dx dy
!^Numpad4:: WinTranslate(-step, 0) ; left (H)
!^Numpad2:: WinTranslate( 0, step) ; down (J)
!^Numpad8:: WinTranslate( 0, -step) ; up (K)
!^Numpad6:: WinTranslate( step, 0) ; right (L)
; Launch programs
!^+t::openProgram(TERMINAL_EXE, TERMINAL_CMD, A_Temp, POS.mainFocus)
!^+b::openProgram(BROWSER_EXE, BROWSER_CMD, A_Temp, POS.mainFocus)