既にtrunkでは修正済みです。1.9.2 branchには直接の原因となった修正が今はまだチェックインされていないので、この修正もまだ入っていません。
原因の究明にはかなり時間がかかりましたが、ちゃんと理解できました。元々、ロジクールのマウスのドライバはGeckoに対してWM_MOUSEHWHEEL
を送信することで横スクロールという結果が発生している訳ではありませんでした。キャレットブラウジングモードに切り替えると分かるのですが、最初のWM_MOUSEHWHEEL
の処理時に::GetMessagePos
を一切利用せずに、常にゼロがセットされているlParam
を見て処理していました。このため、最初の::SendMessage
による反応テストの段階で失敗し、その後はWM_KEYDOWN
メッセージで矢印キーが押されたことにしてスクロールを実現していました(だからキャレットブラウジングモードだとスクロールせずにキャレットが動くのみ)。
ですがtrunkでは、::GetMessagePos
APIを利用するようになったことで、最初の::SendMessage
時にのみ正常なマウスカーソル位置が取得できてしまい、処理が成功したのを確認したロジクールのドライバが::PostMessage
でWM_MOUSEHWHEEL
を連射してくるものの、ここでは::GetMessagePos
APIの戻り値も壊れていて処理に失敗し続けている、というのがこのバグの真相でした。
私のパッチでは、lParam
値や、::GetMessagePos
APIの戻り値から、ロジクールのマウスであるかを推測し、そうであると判断した場合には::GetCursorPos
APIを利用するようにしています。もちろん、これらのAPIの違いの分、誤動作はあるかもしれませんが、よほど処理落ちした状況等以外で誤動作だと分かることもないのではないかと思います。
物議をかもしたマウスホイールによるスクロール速度の問題にひとまず決着がつきました。既に1.9.2 branchにも修正が入っています。
現在の仕様は、ホイールの回転速度に応じた加速(acceleration)はデフォルトでは無効になりました。マウスドライバがこの機能を持っていない環境で有効にしたい場合は、プラットフォームを問わず、mousewheel.acceleration.start
を0以上に設定し、mousewheel.acceleration.factor
で加速度を調整できます。start
は、何回目のホイールイベントから加速を開始するのか、factor
は加速する時の倍率を算出するための係数です。加速された行数の算出は、
PRInt32(NS_round(aDelta * sScrollSeriesCounter * (double)aFactor / 10));
となっています。aDelta
はホイールイベントで指定されたスクロール行数、sScrollSeriesCounter
は80ms以内にホイールイベントが連続している時の、ホイールイベントの発生回数(80ms以内に発生したイベントの回数ではありません、80ms発生しなかったらリセットされますが、80ms以内にホイールイベントが発生すると加算されつづけ、加速し続けます)、aFactor
が上記の係数です。sScrollSeriesCounter
が無造作に大きくなる可能性がある明らかに変なロジックですが、これがそのまま残らずに助かりました。
ですが、無効にして終わりともならないのでシステム設定のスクロール速度をGecko内部で乗算し、上書きするメカニズムを用意しました(override)。ただし、出来る限りユーザの設定を尊重する形で制限が加えてあります。
まず、このメカニズムを有効にするにはmousewheel.system_scroll_override_on_root_content.enabled
をtrue
にします。Windowsではデフォルトで有効になっていますが、他のプラットフォームではデフォルトでオフです。
ただし、この設定で有効にしても、ドキュメントのルートのスクロールでしか上書きは行われません。つまり、textarea
やリストボックス、ツリービュー等のコントロールや、overflow
をauto
やscroll
に指定しただけの要素ではシステムの設定速度でのスクロールのままになります。
さらに、デフォルトで有効なWindowsではシステム設定の速度設定をユーザ、もしくはマウスのドライバが変更していない場合にのみ上書きするように制限しています。つまり、縦、横ともにWindowsのデフォルトである3行(3文字)スクロールの場合にのみ上書きが行われ、どちらかでも3以外の数値になっている場合には上書きされません(XP以前のWindowsでは横方向のホイールはOSレベルではサポートしていなかったため、検証されません。縦方向のみで判断されます)。
このようになった理由は
- ユーザが数値を変更している場合、ユーザの好みの速度なのでそれを尊重すべき
- マウスドライバが数値を変更している場合、そのドライバは加速機能をそもそも持っているのではないかと推測される(Microsoft、Logitech(Logicool)は共に変更し加速機能つき、VAIOのAlpsのタッチパッドでは変更しているものの加速機能はない)
となっています。おそらく後者の方で実際には上書きが発生しない環境がほとんどではないかと思います(素のWindowsで、特殊なドライバを使わない安いマウスでなければなかなか条件には該当しないと思います)。結局、この考え方で救えないのはWindowsそのもののデフォルト設定が好きなユーザです。その人たちには残念ながら上記の設定から無効にしてもらうしかありません。
乗算する際の倍数はmousewheel.system_scroll_override_on_root_content.vertical.factor
とmousewheel.system_scroll_override_on_root_content.horizontal.factor
で、それぞれ垂直方向と水平方向への値を個別に指定できます。値は、100で割ってから利用されるので、初期値の200だと、2.00倍の意味となります。ただし乗算後は整数に四捨五入されるので小数はdelta値がぶれるマウスドライバ(例えばMicrosoftのInteliPoint)との組み合わせでなければあまり意味がないかもしれません。
乗算結果がシステム設定の速度に対する乗算結果を超えた場合、その数値はシステム設定の速度に制限されます。たとえば、システム設定が3行なのに対して、deltaが4のイベントだった場合、4×2で8が採用されるのではなく、3×2の6に制限された値で上書きされます。これにより、マウスドライバが設定を変更しないままに加速機能を持っていた場合、必要以上に加速してしまうことを防止しています。
最後に、これら二つの仕組みを含めた計算結果が1ページ以上スクロールしてしまう場合、1ページのみのスクロールに制限されます。つまり、一回のホイールイベントでスクロールしすぎて行の存在を見過ごすことがないようにしています。