Skip to content

Instantly share code, notes, and snippets.

@spongeboy
Created July 21, 2026 15:07
Show Gist options
  • Select an option

  • Save spongeboy/ad98fa74e2e8367d12ab03073f70f5ec to your computer and use it in GitHub Desktop.

Select an option

Save spongeboy/ad98fa74e2e8367d12ab03073f70f5ec to your computer and use it in GitHub Desktop.
PatSnap
; PATSNAP - Pat shaped Windows Snapping. To copy behaviour of Moom in OSX
; Waits for key press (1/2/3/4), snaps window to specified width
; 1 = 50% Left | 2 = 50% Right | 3 = 75% Left | 4 = 75% Right
#Requires AutoHotkey v2.0
#SingleInstance Force
; --- Startup Message ---
TrayTip("See C:\Users\pat_m\personal\projects\asdf\Docs\autohotkey", "PATSNAP")
^+z:: {
ToolTip("PATSNAP. Esc to exit.")
; Loop: wait for a single key each time; exit on any unexpected key
Loop {
ih := InputHook("L1 T10", "{Escape}{Left}{Right}{Space}{1}{2}{3}{4}")
ih.Start()
ih.Wait()
if (ih.EndReason = "Timeout") {
; No whitelisted key or char yet; if any other key is currently down, exit
if (UnexpectedKeyPressed())
break
continue
}
key := ih.EndKey ; Will be one of the EndKeys, or empty for other chars
if (key = "") {
; Any non-whitelisted character exits snap mode
break
}
if (key = "1") {
SnapLeft50()
continue
}
if (key = "2") {
SnapRight50()
continue
}
if (key = "3") {
SnapLeft75()
continue
}
if (key = "4") {
SnapRight75()
continue
}
if (key = "Space") {
SnapFull()
continue
}
if (key = "Left") {
MoveToMonitor(-1)
continue
}
if (key = "Right") {
MoveToMonitor(1)
continue
}
if (key = "Escape") {
break
}
; Fallback: any other key exits
break
}
ToolTip("")
;TrayTip("Snap mode exited", "Window Snapper", 16)
}
; #####################################
; ### Function Definitions ###
; #####################################
SnapLeft50() {
ShowDebug("Key '1' pressed - Snapping 50% LEFT")
SnapWindow(true, 75)
}
SnapRight50() {
ShowDebug("Key '2' pressed - Snapping 50% RIGHT")
SnapWindow(false, 75)
}
SnapLeft75() {
ShowDebug("Key '3' pressed - Snapping 75% LEFT")
SnapWindow(true, 50)
}
SnapRight75() {
ShowDebug("Key '4' pressed - Snapping 75% RIGHT")
SnapWindow(false, 50)
}
SnapFull() {
ShowDebug("Key space pressed - Snapping 100%")
SnapWindow(false, 100)
}
; --- Move Window to Next/Previous Monitor ---
MoveToMonitor(direction) {
ShowDebug("Moving window to " . (direction > 0 ? "next" : "previous") . " monitor")
; Get the active window
hActive := WinExist("A")
if (!hActive) {
ShowError("No active window found")
return
}
; Don't move system windows
sClass := WinGetClass("ahk_id " . hActive)
if (sClass = "Progman" || sClass = "WorkerW" || sClass = "Shell_TrayWnd") {
ShowDebug("Skipping system window: " . sClass)
return
}
; Get current window position and size
WinGetPos(&winX, &winY, &winW, &winH, "ahk_id " . hActive)
; Get the current monitor
MONITOR_DEFAULTTONEAREST := 0x00000002
hCurrentMonitor := DllCall("MonitorFromWindow", "Ptr", hActive, "UInt", MONITOR_DEFAULTTONEAREST, "Ptr")
; Get monitor count
monitorCount := SysGet(80) ; SM_CMONITORS
if (monitorCount <= 1) {
ShowDebug("Only one monitor detected")
return
}
; Find which monitor number the window is currently on
currentMonitorNum := 0
Loop monitorCount {
MonitorGet(A_Index, &mLeft, &mTop, &mRight, &mBottom)
; Get monitor handle for this monitor
MONITORINFO := Buffer(40, 0)
NumPut("UInt", 40, MONITORINFO, 0)
; Create a point in the center of this monitor
centerX := mLeft + (mRight - mLeft) // 2
centerY := mTop + (mBottom - mTop) // 2
; Get the monitor handle at this point
hTestMonitor := DllCall("MonitorFromPoint", "Int64", (centerY << 32) | (centerX & 0xFFFFFFFF), "UInt", MONITOR_DEFAULTTONEAREST, "Ptr")
if (hTestMonitor = hCurrentMonitor) {
currentMonitorNum := A_Index
break
}
}
if (currentMonitorNum = 0) {
ShowError("Could not determine current monitor")
return
}
; Calculate target monitor number (wrap around)
targetMonitorNum := currentMonitorNum + direction
if (targetMonitorNum > monitorCount)
targetMonitorNum := 1
else if (targetMonitorNum < 1)
targetMonitorNum := monitorCount
ShowDebug("Moving from monitor " . currentMonitorNum . " to monitor " . targetMonitorNum)
; Get target monitor work area
MonitorGetWorkArea(targetMonitorNum, &workLeft, &workTop, &workRight, &workBottom)
; Calculate target position (try to maintain relative position)
MonitorGetWorkArea(currentMonitorNum, &currWorkLeft, &currWorkTop, &currWorkRight, &currWorkBottom)
currWorkW := currWorkRight - currWorkLeft
currWorkH := currWorkBottom - currWorkTop
targetWorkW := workRight - workLeft
targetWorkH := workBottom - workTop
; Calculate relative position on current monitor (0.0 to 1.0)
relX := (winX - currWorkLeft) / currWorkW
relY := (winY - currWorkTop) / currWorkH
; Calculate size ratio
relW := winW / currWorkW
relH := winH / currWorkH
; Apply to target monitor
newX := workLeft + Round(relX * targetWorkW)
newY := workTop + Round(relY * targetWorkH)
newW := Round(relW * targetWorkW)
newH := Round(relH * targetWorkH)
; Move the window
try {
WinMove(newX, newY, newW, newH, "ahk_id " . hActive)
ShowDebug("Window moved successfully to monitor " . targetMonitorNum)
} catch as err {
ShowError("Failed to move window - Error: " . err.Message)
return
}
}
; --- Main Worker Function for Snapping ---
SnapWindow(isLeft, widthPercent) {
ShowDebug("SnapWindow called - Direction: " . (isLeft ? "Left" : "Right") . " Width: " . widthPercent . "%")
; 1. Get the handle of the currently active window
hActive := WinExist("A")
ShowDebug("Window Handle: " . hActive)
if (!hActive) {
ShowError("No active window found")
return
}
; Don't snap if it's the desktop or taskbar
sClass := WinGetClass("ahk_id " . hActive)
ShowDebug("Window Class: " . sClass)
; Only skip actual desktop and taskbar windows
if (sClass = "Progman" || sClass = "WorkerW" || sClass = "Shell_TrayWnd") {
ShowDebug("Skipping system window: " . sClass)
return
}
; Restore window if maximized
minMax := WinGetMinMax("ahk_id " . hActive)
if (minMax = 1) {
ShowDebug("Window is maximized, restoring...")
WinRestore("ahk_id " . hActive)
Sleep(100)
}
; 2. Get the monitor handle associated with the active window
MONITOR_DEFAULTTONEAREST := 0x00000002
hMonitor := DllCall("MonitorFromWindow", "Ptr", hActive, "UInt", MONITOR_DEFAULTTONEAREST, "Ptr")
ShowDebug("Monitor Handle: " . hMonitor)
if (!hMonitor) {
ShowError("Failed to get monitor handle")
return
}
; 3. Get monitor information using direct WinAPI call
; Create MONITORINFO structure (40 bytes)
MONITORINFO := Buffer(40, 0)
NumPut("UInt", 40, MONITORINFO, 0) ; cbSize
; Call GetMonitorInfo
result := DllCall("GetMonitorInfo", "Ptr", hMonitor, "Ptr", MONITORINFO)
if (!result) {
ShowError("Failed to get monitor information")
return
}
ShowDebug("Monitor info retrieved")
; 4. Extract monitor dimensions
; Structure: cbSize(4) + rcMonitor(16) + rcWork(16) + dwFlags(4)
; rcMonitor: left(4), top(4), right(4), bottom(4) at offsets 4,8,12,16
; rcWork: left(4), top(4), right(4), bottom(4) at offsets 20,24,28,32
iMonLeft := NumGet(MONITORINFO, 4, "Int")
iMonTop := NumGet(MONITORINFO, 8, "Int")
iMonRight := NumGet(MONITORINFO, 12, "Int")
iMonBottom := NumGet(MONITORINFO, 16, "Int")
ShowDebug("Full Monitor: L=" . iMonLeft . " T=" . iMonTop . " R=" . iMonRight . " B=" . iMonBottom)
; Work area (excludes taskbar)
iWorkLeft := NumGet(MONITORINFO, 20, "Int")
iWorkTop := NumGet(MONITORINFO, 24, "Int")
iWorkRight := NumGet(MONITORINFO, 28, "Int")
iWorkBottom := NumGet(MONITORINFO, 32, "Int")
ShowDebug("Work Area: L=" . iWorkLeft . " T=" . iWorkTop . " R=" . iWorkRight . " B=" . iWorkBottom)
; Use work area if valid, otherwise fall back to full monitor
if (iWorkRight > iWorkLeft && iWorkBottom > iWorkTop) {
iMonitorX := iWorkLeft
iMonitorY := iWorkTop
iMonitorW := iWorkRight - iWorkLeft
iMonitorH := iWorkBottom - iWorkTop
ShowDebug("Using Work Area")
} else {
iMonitorX := iMonLeft
iMonitorY := iMonTop
iMonitorW := iMonRight - iMonLeft
iMonitorH := iMonBottom - iMonTop
ShowDebug("Using Full Monitor Area")
}
ShowDebug("Final: X=" . iMonitorX . " Y=" . iMonitorY . " W=" . iMonitorW . " H=" . iMonitorH)
; Check if dimensions are valid
if (iMonitorW <= 0 || iMonitorH <= 0) {
ShowError("Invalid monitor dimensions: W=" . iMonitorW . " H=" . iMonitorH)
return
}
; 5. Calculate the new position and size based on percentage
iWindowW := Floor(iMonitorW * widthPercent / 100)
iNewY := iMonitorY
if (isLeft) {
; Left Side: Starts at the monitor's work area X position
iNewX := iMonitorX
} else {
; Right Side: Starts at the monitor's X position PLUS the remaining width
iNewX := iMonitorX + (iMonitorW - iWindowW)
}
ShowDebug("Moving window to: X=" . iNewX . " Y=" . iNewY . " W=" . iWindowW . " H=" . iMonitorH)
; 6. Move and resize the active window
try {
WinMove(iNewX, iNewY, iWindowW, iMonitorH, "ahk_id " . hActive)
ShowDebug("Window snapped successfully!")
} catch as err {
ShowError("Failed to move window - Error: " . err.Message)
return
}
}
; --- Error Display Function ---
ShowError(sMessage) {
ToolTip("ERROR: " . sMessage)
Sleep(2000)
ToolTip()
}
; --- Debug Display Function ---
ShowDebug(sMessage) {
;ToolTip("DEBUG: " . sMessage)
;Sleep(1500)
}
; Returns true if any unexpected key (not handled by snap mode) is detected as down
UnexpectedKeyPressed() {
; Virtual-key ranges to check broadly
vkRanges := [ [0x08,0x2F], [0x5B,0xFF] ]
; Allowed keys' virtual key codes
allowedVK := Map(
0x31, true, ; '1'
0x32, true, ; '2'
0x33, true, ; '3'
0x34, true, ; '4'
0x20, true, ; Space
0x25, true, ; Left
0x27, true, ; Right
0x1B, true, ; Escape
; Modifiers (ignore if still held from hotkey): Shift/Ctrl/Alt and L/R variants
0x10, true, ; Shift
0x11, true, ; Control
0x12, true, ; Alt (Menu)
0xA0, true, ; LShift
0xA1, true, ; RShift
0xA2, true, ; LControl
0xA3, true, ; RControl
0xA4, true, ; LMenu (Alt)
0xA5, true ; RMenu (Alt)
)
for range in vkRanges {
start := range[1]
finish := range[2]
vk := start
while (vk <= finish) {
if !allowedVK.Has(vk) {
state := DllCall("GetAsyncKeyState", "Int", vk, "Short")
if (state & 0x8000) {
return true
}
}
vk++
}
}
return false
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment