というか AutoHotkey 2 対応。関数ネストとかファットアローとか、まあまあ JS みたいに書ける。もう v1 で書く気にはならない。
あと、トラックボールが壊れたのでエレコム HUGE というやつに買いかえた。
これは中央ボタンよりも forward ボタンの方が押しやすいので、XButton2 をトリガーにして使っている。
#Requires AutoHotkey v2.0
#Warn
CoordMode("Mouse", "Screen")
; 設定 (グローバル変数は各関数内で変更不可能らしい?)
errortip := true ; 例外を ToolTip に表示したければ
swipelike := true ; スクロールバードラッグよりスワイプに似せるなら
hidecursor := true ; 押しながら移動のときカーソル消去するなら
simclick := "LButton" ; 長押しではないとき、これを押したことにする
maxcount := 10 ; 一回のループあたりのホイール回数制限
; https://learn.microsoft.com/windows/win32/inputdev/wm-mousewheel
halfdelta := 60 ; なぜか知らんが WHEEL_DELTA の半分で1行になるっぽい
; 移動による加速の加減
moveaccel := (delta) => delta * Abs(delta) ; 適当に2乗でいいかな
; ホイール速度による加速の加減
; 適当に間隔700ms未満から加速 https://www.desmos.com/calculator/njyks9wsta
wheelaccel := (one) => one * (1 + (Max(0, 700 - A_TimeSincePriorHotkey) / 300) ** 3)
; 各種のウェイトをなしにする
SetKeyDelay(-1, -1)
SetMouseDelay(-1)
SetDefaultMouseSpeed(0)
SetWinDelay(-1)
; ボタンを押しながらマウス移動でホイール動作のふり
; XButton1(戻るボタン)は使うけど2(進む)って使わなくない?
*XButton2::
{
; 最初のモディファイア部分を消した純粋なボタン名
thiskey := RegExReplace(A_ThisHotkey, "\W")
; エラー消す
If (errortip)
ToolTip
MouseGetPos(&sx, &sy, &id, &ctrl, 1)
If (ctrl == "")
ctrl := WinGetClass("ahk_id " id)
uwp := exceptional(ctrl, id)
setcursor(false)
moved := false
sumdelta := 0
While GetKeyState(thiskey, "P")
{
MouseGetPos(&x, &y)
delta := swipelike ? y - sy : sy - y
If (delta == 0)
Continue
If (!uwp)
{
PostMessage(0x20A,
(moveaccel(delta) << 16) | mods(),
(sy << 16) | sx,
ctrl, "ahk_id " id)
}
Else
{
sumdelta += moveaccel(delta)
wheelcount := Integer(sumdelta / halfdelta)
If (wheelcount != 0)
{
SendInput("{Blind}{"
. (wheelcount > 0 ? "WheelUp" : "WheelDown")
. " " Min(maxcount, Abs(wheelcount))
. "}")
sumdelta := 0
}
}
moved := true
MouseMove(sx, sy, 0)
}
setcursor(true)
; 押してすぐ離した場合など
If (!moved)
SendInput("{Blind}{" . (simclick ? simclick : thiskey) . "}")
Return
; カーソルを見せたり見せなかったりしろ
setcursor(visible)
{
static AFF := Buffer(32 * 4, 0xFF)
static X00 := Buffer(32 * 4, 0x00)
; カーソルは再利用できず、毎回作成しなければいけないみたい
static nullcursor := () => DllCall("CreateCursor", "UInt", 0,
"Int", 0, "Int", 0, "Int", 32, "Int", 32,
"Ptr", AFF, "Ptr", X00)
If (!hidecursor)
Return
If (visible)
DllCall("SystemParametersInfo", "UInt", 0x57, "UInt", 0, "UInt", 0, "UInt", 0)
Else
{
; サイズ変更と砂時計系は変更しないでおく
; https://learn.microsoft.com/windows/win32/api/winuser/nf-winuser-setsystemcursor
DllCall("SetSystemCursor", "Ptr", nullcursor(), "Int", 32512) ; ノーマル
DllCall("SetSystemCursor", "Ptr", nullcursor(), "Int", 32513) ; 文字Iビーム
DllCall("SetSystemCursor", "Ptr", nullcursor(), "Int", 32649) ; リンク上の指
}
}
}
; ホイールの加速
; cf. https://www6.atwiki.jp/eamat/pages/32.html
*WheelUp::
*WheelDown::
{
; 最初のモディファイア部分を消した純粋なボタン名
thiskey := RegExReplace(A_ThisHotkey, "\W")
MouseGetPos(&x, &y, &id, &ctrl, 1)
If (ctrl == "")
ctrl := WinGetClass("ahk_id " id)
v := (thiskey == "WheelUp") ? 1 : -1
If (A_PriorHotkey == A_ThisHotkey)
v := wheelaccel(v)
If (!exceptional(ctrl, id))
PostMessage(0x20A,
Integer(halfdelta * v) << 16 | mods(),
(y << 16) | x,
ctrl, "ahk_id " id)
Else
SendInput("{Blind}{" thiskey " " Abs(Integer(v)) "}")
}
; PostMessageが使えるかどうか確認
exceptional(class, id)
{
static tttext := ""
postable := true
try
PostMessage(0, , , class, "ahk_id " id)
catch TargetError
{
postable := false
If (errortip and !(tttext ~= " " class))
{
tttext .= " " class
ToolTip("TargetError classes:" tttext)
}
}
; UWP?
Return (class ~= "^Windows\.UI\.Core\.CoreWindow") or !postable
}
; Ctrl押しながらでズームとかできるように
mods()
{
static modkeys := [
"LButton",
"RButton",
"Shift",
"Ctrl",
"MButton",
"XButton1",
"XButton2"
]
rv := 0
For i, v in modkeys
rv |= (1 << (i - 1)) * GetKeyState(v)
Return rv
}
; おまけ: マウスカーソル直下ウィンドウの最大化トグル
AppsKey::
{
MouseGetPos(, , &id)
If (WinGetMinMax("ahk_id " id))
WinRestore("ahk_id " id)
Else
WinMaximize("ahk_id " id)
}
Fn1 ボタンを AppsKey にして、Fn2 を Alt+Esc にして使ってみている。アプリケーションによっては Ctrl+C と Ctrl+V にしても良さそう。