Skip to content

Instantly share code, notes, and snippets.



Created Sep 15, 2013
What would you like to do?
DPI-aware FocuslessScroll
; Modified by Belleve Invis
; In order to support High DPI environment
; FocuslessScroll by Scoox
; Source:
; Modifications by Geoff Stokes
#SingleInstance Force
#MaxHotkeysPerInterval 150 ;Avoid warning when mouse wheel turned very fast
Menu, Tray, Tip, FocuslessScroll by Scoox
Menu, Tray, Icon, Shell32.dll, 123
;Autoexecute code
IniRead, MinLinesPerNotch, config.ini, FocuslessScroll, linespernotchmin, 1
IniRead, MaxLinesPerNotch, config.ini, FocuslessScroll, linespernotchmax, 3
IniRead, AccelerationThreshold, config.ini, FocuslessScroll, accelerationthreshold, 100
IniRead, AccelerationType, config.ini, FocuslessScroll, accelerationtype, "P"
IniRead, StutterThreshold, config.ini, FocuslessScroll, stutterthreshold, 10
IniRead, NaturalScrolling, config.ini, FocuslessScroll, naturalscrolling, true ; Natural Scrolling, like touch devices
IniRead, EmulateStandardWScrollLock, config.ini, FocuslessScroll, emulatestandardwithscrolllock, true ; Emulate default Windows scrolling when Scroll Lock is on (3-line, traditional direction)
IniRead, TitlebarMinimise, config.ini, FocuslessScroll, titlebarminimise, true
;Function definitions
;See above for details
FocuslessScroll(MinLinesPerNotch, MaxLinesPerNotch, AccelerationThreshold, AccelerationType, StutterThreshold, NaturalScrolling, EmulateStandardWScrollLock, TitlebarMinimise)
SetBatchLines, -1 ;Run as fast as possible
CoordMode, Mouse, Screen ;All coords relative to screen
SetMouseDelay, -1
SetKeyDelay, -1
If(%EmulateStandardWScrollLock% == true) AND (GetKeyState("ScrollLock", "T"))
MinLinesPerNotch := 3
MaxLinesPerNotch := 3
NaturalScrolling := false
;Stutter filter: Prevent stutter caused by cheap mice by ignoring successive WheelUp/WheelDown events that occur to close together.
If(A_TimeSincePriorHotkey < StutterThreshold) ;Quickest succession time in ms
If(A_PriorHotkey = "WheelUp" Or A_PriorHotkey ="WheelDown")
VarSetCapacity(pp, 8, 0)
DllCall("GetPhysicalCursorPos", ptr, &pp)
p_x := NumGet(pp, 0, "uint32")
p_y := NumGet(pp, 4, "uint32")
ControlClass1 := DllCall("WindowFromPhysicalPoint", "int64", (p_y << 32) | (p_x & 0xFFFFFFFF), "Ptr") ;32-bit and 64-bit support
DllCall("PhysicalToLogicalPoint", "uint", ControlClass1, ptr, &pp)
m_x := NumGet(pp, 0, "uint32")
m_y := NumGet(pp, 4, "uint32")
lParam := (m_y << 16) | (m_x & 0x0000FFFF)
delta := 120 ;Wheel delta is 120, as defined by MicroSoft
If(%NaturalScrolling% == true) ; hack to invert for natural scrolling
delta := -delta
;Detect WheelDown event
If(A_ThisHotkey = "WheelDown" Or A_ThisHotkey = "^WheelDown" Or A_ThisHotkey = "+WheelDown" Or A_ThisHotkey = "*WheelDown")
delta := -delta ;If scrolling down, invert scroll direction
;Adjust lines per notch according to scrolling speed
Lines := LinesPerNotch(MinLinesPerNotch, MaxLinesPerNotch, AccelerationThreshold, AccelerationType)
delta := delta * Lines
wParam := (delta << 16)
;Detect modifer keys held down (only Shift and Control work)
wParam := wParam | 0x4
wParam := wParam | 0x8
SendMessage, 0x20A, wParam, lParam,, ahk_id %ControlClass1%
;All parameters are the same as the parameters of FocuslessScroll()
;Return value: Returns the number of lines to be scrolled calculated from the current scroll speed.
LinesPerNotch(MinLinesPerNotch, MaxLinesPerNotch, AccelerationThreshold, AccelerationType)
T := A_TimeSincePriorHotkey
;Normal slow scrolling, separationg between scroll events is greater than AccelerationThreshold miliseconds.
If((T > AccelerationThreshold) Or (T = -1)) ;T = -1 if this is the first hotkey ever run
Lines := MinLinesPerNotch
;Fast scrolling, use acceleration
If(AccelerationType = "P")
;Parabolic scroll speed curve
;f(t) = At^2 + Bt + C
A := (MaxLinesPerNotch-MinLinesPerNotch)/(AccelerationThreshold**2)
B := -2 * (MaxLinesPerNotch - MinLinesPerNotch)/AccelerationThreshold
C := MaxLinesPerNotch
Lines := Round(A*(T**2) + B*T + C)
;Linear scroll speed curve
;f(t) = Bt + C
B := (MinLinesPerNotch-MaxLinesPerNotch)/AccelerationThreshold
C := MaxLinesPerNotch
Lines := Round(B*T + C)
Return Lines
;All hotkeys with the same parameters can use the same instance of FocuslessScroll(). No need to have separate calls unless each hotkey requires different parameters (e.g. you want to disable acceleration for Ctrl-WheelUp and Ctrl-WheelDown). If you want a single set of parameters for all scrollwheel actions, you can simply use *WheelUp:: and *WheelDown:: instead.
#MaxThreadsPerHotkey 6 ;Adjust to taste. The lower the value, the lesser the momentum problem on certain smooth-scrolling GUI controls (e.g. AHK helpfile main pane, WordPad...), but also the lesser the acceleration feel. The good news is that this setting does no affect most controls, only those that exhibit the momentum problem. Nice.
;Scroll with acceleration
WheelDown::FocuslessScroll(MinLinesPerNotch, MaxLinesPerNotch, AccelerationThreshold, AccelerationType, StutterThreshold, NaturalScrolling, EmulateStandardWScrollLock, TitlebarMinimise)
;Ctrl-Scroll zoom with no acceleration (MaxLinesPerNotch = MinLinesPerNotch).
^WheelDown::FocuslessScroll(MinLinesPerNotch, MinLinesPerNotch, AccelerationThreshold, AccelerationType, StutterThreshold, NaturalScrolling, EmulateStandardWScrollLock, TitlebarMinimise)
;If you want zoom acceleration, replace above line with this:
;FocuslessScroll(MinLinesPerNotch, MaxLinesPerNotch, AccelerationThreshold, AccelerationType, StutterThreshold, NaturalScrolling, EmulateStandardWScrollLock, TitlebarMinimise)
#MaxThreadsPerHotkey 1 ;Restore AHK's default value i.e. 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment