AutoHotkey Alt-Click Window Drag
AutoHotkey is an automation scripting language for Windows. It can be used to create macros and hotkeys, create autocorrect replacements, and even GUIs.
The following script mimics how Linux can move windows around by Alt+Clicking anywhere on the window. It also ignores maximized windows and VNC viewers.
The first part ensures that the script only runs once. It then assigns a hotkey to this script, specifically the Alt button (!
)and the left mouse button (LButton
).
#SingleInstance force
!LButton::
Next, MouseGetPos
is used to get a window ID for the window under the cursor.
; Get window under cursor
MouseGetPos, , , id
Then, some abort logic occurs — if the window is maximized or the current app is a VNC viewer, do not move the window. Maximized windows cannot be moved, since they are maximized. VNC viewers should also be ignored, since typically I’m running Linux and want to use their window managing system.
To determine if a window is maximized, I use WinGet
with the MinMax
subcommand to get the minimized/maximized state.
To figure out if an application is a VNC viewer, I use WinGetClass
to retrieve the window’s class. I then use InStr
to see if “vnc” is within the class name.
If either of the above is true, the ignore
flag is set to true. Rather than aborting the script, I chose to pass the Alt + left mouse button through. When those buttons are released, I then release the buttons to the OS (later in the script).
; Abort if window is maximized
ignore := flase
WinGet, window_minmax, MinMax, ahk_id %id%
if (window_minmax <> 0) {
ignore := true
}
; Abort if app is a vnc viewer
WinGetClass, class, A
if (InStr(class, "vnc")) {
ignore := true
}
; Send Alt + left mouse button (if not ignoring)
if (ignore) {
Send {Alt down}{LButton down}
}
After that, I do some initial setup to get the mouse position relative to the screen (CoordMode
and MouseGetPos
). WinGetPos
is then used to get the X and Y coordinates of the window’s upper left corner relative to the screen. I then adjust the window coordinates by the mouse coordinates to get the initial window position relative to the mouse cursor.
The SetWinDelay
command causes the movement to occur immediately.
I then activate the window with WinActivate
and set the transparency with WinSet
to show that the window is currently being moved.
; Get mouse and window position
CoordMode, Mouse, Screen
MouseGetPos, mouse_x, mouse_y
WinGetPos, win_x, win_y, , , ahk_id %id%
; Change win_x/win_y based on initial mouse position
win_x := mouse_x - win_x
win_y := mouse_y - win_y
; Reduce latency of movements
SetWinDelay, 0
; Set the transparency
if (!ignore) {
WinActivate, ahk_id %id%
WinSet Transparent, 150, ahk_id %id%
}
The main loop will continuously run until the left mouse button was released (GetKeyState
). If the mouse wasn’t released and as long as we are not ignoring the hotkey, the window is moved by taking the mouse position (MouseGetPos
) and subtracting the window position from it – that coordinate is then passed into WinMove
to perform the movement.
; Loop until left button released
loop {
if !GetKeyState("LButton", "P") {
break
}
if (!ignore) {
MouseGetPos, mouse_x, mouse_y
WinMove ahk_id %id%, , (mouse_x - win_x), (mouse_y - win_y)
}
}
The final part disables the transparency with WinSet
, then releases the Alt + left mouse button with Send
. The return
exits the script.
; Reset the transparency
WinSet Transparent, Off, ahk_id %id%
; Release Alt + left mouse button
Send {Alt up}{LButton up}
return
Here is the full script.
; alt-click-move.ahk
;
; When Alt + left mouse button is pressed, move the window under the cursor,
; as long as the window is not maximized, or a vnc viewer.
;
; Cody Cziesler
#SingleInstance force
!LButton::
; Get window under cursor
MouseGetPos, , , id
; Abort if window is maximized
ignore := flase
WinGet, window_minmax, MinMax, ahk_id %id%
if (window_minmax <> 0) {
ignore := true
}
; Abort if app is a vnc viewer
WinGetClass, class, A
if (InStr(class, "vnc")) {
ignore := true
}
; Send Alt + left mouse button (if not ignoring)
if (ignore) {
Send {Alt down}{LButton down}
}
; Get mouse and window position
CoordMode, Mouse, Screen
MouseGetPos, mouse_x, mouse_y
WinGetPos, win_x, win_y, , , ahk_id %id%
; Change win_x/win_y based on initial mouse position
win_x := mouse_x - win_x
win_y := mouse_y - win_y
; Reduce latency of movements
SetWinDelay, 0
; Set the transparency
if (!ignore) {
WinActivate, ahk_id %id%
WinSet Transparent, 150, ahk_id %id%
}
; Loop until left button released
loop {
if !GetKeyState("LButton", "P") {
break
}
if (!ignore) {
MouseGetPos, mouse_x, mouse_y
WinMove ahk_id %id%, , (mouse_x - win_x), (mouse_y - win_y)
}
}
; Reset the transparency
WinSet Transparent, Off, ahk_id %id%
; Release Alt + left mouse button
Send {Alt up}{LButton up}
return