LoginSignup
12
11

More than 5 years have passed since last update.

WheelBallをAutoHotKeyで再現する

Last updated at Posted at 2018-06-18

トラックボーラーにとって必須ソフトウェアであるWheelBallですが、
だいぶ昔のソフトウェアということもあり、Windows10では動作が不安定です。
(作者のkamigamiさんも更新予定はないと表明しています。 http://www.kamigaki.net/blog/?p=12)

そこで、後発のW10Wheel.NETが開発されています。
このソフトウェアを利用しようとしたのですが、社内のセキュリティソフトに引っかかってしまいました :sob:

ということで、なければ作れば良いじゃないの精神でAutoHotKeyを使って再現してみました :sunglasses:

※ 自分が使える分の機能をそれらしく再現しただけで完全再現ではないです
※ コードだけみたい方はこちら

要件

WheelBallのようなスクロールをエミュレートするソフトウェアに求めている要件は以下のものです。

  • マウスの右ボタンを押している間にカーソル(ボール)を動かすとスクロール扱いになる
    • 公開するなら設定できるようにしたほうが良いが、まずは自分用に右クリック固定
  • スクロール中はカーソルが動かない
  • カーソルの移動量とスクロールの移動量が連動する
  • 横方向のスクロールに対応
    • エクセルでできなくても良い(そこまで使わないので)
  • 縦スクロールと横スクロールは移動量が多い方を使う
  • 右クリック中にボールが動かされない場合は、通常の右クリックとする

既存の実装

ゼロベースから作り上げるのは大変なので、AutoHotKeyで似たようなものがないか探してみました。
すると以下のページがヒットしました。

使ってみたところやりたいことはほとんど同じで、これを改良すれば望むものが得られそうです。
ここから改良すべき点は以下の3点。

  • カーソルの移動量とスクロールの移動量が連動する
  • 縦スクロールと横スクロールは移動量が多い方を使う
  • 右クリック中にボールが動かされない場合は、通常の右クリックとする

カーソルの移動量とスクロールの移動量が連動する

まずは使用感に大きく作用する移動量に手を入れていきます。

既存のMouseWheelEmulatorは移動量に応じてスクロール量を変えているのではなく、
マウスの移動イベントを一定時間毎に検知し、その間の移動量が閾値を超えたら1回スクロールイベントを発火するという実装になっています。

具体的には96行目からのInputMsg(wParam, lParam)メソッドで行なわれています。

InputMsg(wParam, lParam) {
    local x, y
    Critical

    x := HID_GetInputInfo(lParam, II_MSE_LASTX)
    y := HID_GetInputInfo(lParam, II_MSE_LASTY)
    If ((Abs(x) > 0.0) or (Abs(y) > 0.0))
       mouse_Moved = y
    if y > %mouse_Threshold%
    {
        ScrollDown()
    }
    else if y < -%mouse_Threshold%
        ScrollUp()
    if x > %mouse_Threshold%
    {
        ScrollRight()
    }
    else if x < -%mouse_Threshold%
        ScrollLeft()
    ;ToolTip, % "dX = " . x . "  " . "dY = " . y . a_tab . winID . a_tab . control . a_tab . procName . a_tab . hw_m_target . a_tab . scrollMode
    ;; Uncomment the above line for handy debug info shown while scrolling
}

InputMsgが呼び出されるタイミング、頻度は外部依存です。

このため、移動量に関わらず閾値を超えていれば一定のスクロールをするという動きになっています。
これを移動量を加味した計算に変更します。

具体的にはカーソルの移動量をある定数で割った値でスクロール量を決めます。

以下が修正後のコードです。(コードフォーマットが少し変わっています)

InputMsg(wParam, lParam) {
  ; 省略

  if y > %mouse_Threshold%
    ;; 移動量に重み付けしてスクロールイベントを発火
    loop, % Abs(1 + y//mouse_delta)
      ScrollDown()
  else if y < -%mouse_Threshold%
    loop, % Abs(-1 + y//mouse_delta)
      ScrollUp()

  if x > %mouse_Threshold%
    loop, % Abs(1 + x//mouse_delta)
      ScrollRight()
  else if x < -%mouse_Threshold%
    loop, % Abs(-1 + x//mouse_delta)
      ScrollLeft()

  ; ToolTip, % "dX = " . x . " " . "dY = " . dy . a_tab . winID . a_tab . control . a_tab . procName . a_tab . hw_m_target . a_tab . scrollMode
  ;; Uncomment the above line for handy debug info shown while scrolling
}

mouse_deltaが重み付けの定数です。
これで移動量が加味されるようになりました!

縦スクロールと横スクロールは移動量が多い方を使う

既存の実装では斜めに動かすと縦横両方のスクロールイベントが発火します。
ブラウザによっては横スクロールでブラウザバックを発火させてしまうので、移動量が多い方のみを選択するようにします。
これもInputMsgを修正します。
スクロールイベントを発火する前に、移動量の大きい方のみを選択します。

InputMsg(wParam, lParam) {
  ; 省略

  ;; ここで大きい方を判断する
  if Abs(x) > Abs(y)
  {
    if x > %mouse_Threshold%
      loop, % Abs(1 + x//mouse_delta)
        ScrollRight()
    else if x < -%mouse_Threshold%
      loop, % Abs(-1 + x//mouse_delta)
        ScrollLeft()
  }
  else
  {
    if y > %mouse_Threshold%
      loop, % Abs(1 + y//mouse_delta)
        ScrollDown()
    else if y < -%mouse_Threshold%
      loop, % Abs(-1 + y//mouse_delta)
        ScrollUp()
  }

  ; ToolTip, % "dX = " . x . " " . "dY = " . dy . a_tab . winID . a_tab . control . a_tab . procName . a_tab . hw_m_target . a_tab . scrollMode
  ;; Uncomment the above line for handy debug info shown while scrolling
}

右クリック中にカーソルが動かされない場合は、通常の右クリックとする

右クリックの長押しをトリガーにしていますが、通常の右クリックが使えなくなっては困ります。
そこで、右クリック押している間にカーソルが動かなかったら、右クリックのイベントを発火させます。

既存の実装では動きがなかった場合にMiddleボタンのクリックイベントを発火しています。
(88行目からのコード)

scrollChord_Up:
ToolTip
BlockInput, MouseMoveOff
HID_Register(1,2,0,RIDEV_REMOVE)
if mouse_Moved = n
    MouseClick, Middle
return

これを右クリックに変更します。

scrollChord_Up:
  ; 省略
  if mouse_Moved = n
    ;; MiddleからRIGHTに変更
    MouseClick, RIGHT
return

細かい調整

これまでの修正でおおむね自分のやりたいことは再現できたのですが、
ボールの移動量に対してスクロールが早すぎるので調整していきます。

調整するのはInputMsgメソッドの間引きと、移動量のスクロールイベント数に対する重み付けです。
InputMsgメソッドの呼び出しを間引くために、メソッドの最後にSleepを挿入します。

InputMsg(wParam, lParam) {
  ; 省略

  ; ToolTip, % "dX = " . x . " " . "dY = " . dy . a_tab . winID . a_tab . control . a_tab . procName . a_tab . hw_m_target . a_tab . scrollMode
  ;; Uncomment the above line for handy debug info shown while scrolling

  ; 一定時間スリープさせてスクロールイベントの発火数を押さえる
  Sleep, % sleep_interval
}

あとはmouse_deltasleep_intervalを調整していきます。
地味な作業ですが、数値を動かしながら試していく他ありません。

最終的にはmouse_delta3sleep_interval30にすることで落ち着きました。
また、スクロールイベント発火の閾値(mouse_Threshold)も3から1に変更しています。

おわり

今回はAutoHotKeyを使ってWheelBallを再現してみました。
スクロールイベントのエミュレートまでできるなんで、本当にAutoHotKeyは奥が深いですね。

最終的なコードはAHKWheelBallにおいてありますので、よかったら使ってみてください。 (プルリク大歓迎です。)
使う際はご使用の環境に合わせて調整してみてください。

ちなみに自分はCST2545-5Wというトラックボールでビリヤードの5番を使っています。
(オレンジが好き)

12
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
11