というか 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 にしても良さそう。