Linux(X11)でKensingtonのExpert Mouse 7を使う

KensingtonのExpert Mouse 7(EM7)には4つボタンがあり、以下のようにレイアウトされています。

.----------. .---------.
| 2:Middle | | 8:Back  |
`----------' `---------'
.----------. .---------.
| 1:Left   | | 3:Right |
`----------' `---------'

EM7にはスクロールホイールがないため、左上が中央ボタンで、右上が戻るボタンという変則的なレイアウトになっているようです。
このままでは使いづらいので、左上を戻るボタン、右上を進むボタンに設定します。ドライバはevdevを使ってxorg.confの該当セクションにButtonMappingを以下のように記述します。

	Option "ButtonMapping" "1 8 3 4 5 6 7 9 2"

これで一般的な5ボタンマウスのレイアウトになりました。

.--------. .-----------.
| 2:Back | | 8:Forward |
`--------' `-----------'
.--------. .-----------.
| 1:Left | | 3:Right   |
`--------' `-----------'

注意すべきなのは、ButtonMappingでボタンのレイアウトを変えてもインデックスは変わらないという点です。

機能の割り当て

次に、各ボタンに機能を割り当てていきます。

EM7にはスクロールリングが付いていますが、ボタンを押しながらボールを転がすことでスクロールができるとより便利です。自分は、文章をじっくり読むときはスクロールリング、長いページから目当ての情報を探しだすときはボールでスクロールと使い分けています。ここでは、右上のボタン(8:Forward)をスクロールボタンに割り当てています。

	Option "EmulateWheel" "true"
	Option "EmulateWheelButton" "8"
	Option "EmulateWheelInertia" "20"
	Option "XAxisMapping" "6 7"
	Option "YAxisMapping" "4 5"

続いて、先程ButtonMappingで潰してしまった中央ボタンを、左右の同時クリックに割り当てます。

	Option "Emulate3Buttons" "true"

しかし、この設定だけでは意図した通り動作せず、実際には中央ボタンではなく戻るボタンが割り当てられてしまいます。

ここで一旦Emulate3Buttonsの動作を確認してみます。

  1. 左右の同時クリック検知する
  2. "ボタン2"が押されたというイベントを生成
  3. xf86PostButtonEvent()でイベントをポストする

ここで問題となるのが、xf86PostButtonEvent()がボタンの種類ではなくインデックスを取る点です。つまり、ButtonMappingでボタン2を別のボタンに置き換えてしまうと、Emulate3Buttonsで呼ばれるボタンもまた変化してしまいます。

これを修正するにはxf86PostButtonEvent()が呼ばれる前にMappingテーブルを走査して、中央ボタンが割り当てられたインデックスを探し出す必要があります。毎回テーブルを走査するのは無駄なので予めインデックスを格納しておけばよさそうですが、ButtonMappingはxmodmapなどでリアルタイムに変更できるので、それもまた問題があります。

とりあえず面倒なので、evdevのソースに中央ボタンを割り当てたインデックス"9"を決め打ちしました。以下にそのパッチを掲載します。

追記

ちゃんとしたパッチ書きました。

diff --git a/src/emuMB.c b/src/emuMB.c
index 764b30e..287edc6 100644
--- a/src/emuMB.c
+++ b/src/emuMB.c
@@ -96,7 +96,7 @@ static signed char stateTab[11][5][3] = {
     {  0,  0,  0 },   /* nothing -> ground (no change) */
     {  0,  0,  1 },   /* left -> delayed left */
     {  0,  0,  2 },   /* right -> delayed right */
-    {  2,  0,  3 },   /* left & right (middle press) -> pressed middle */
+    {  9,  0,  3 },   /* left & right (middle press) -> pressed middle */
     {  0,  0, -1 }    /* timeout N/A */
   },
 /* 1 delayed left */
@@ -104,7 +104,7 @@ static signed char stateTab[11][5][3] = {
     {  1, -1,  0 },   /* nothing (left event) -> ground */
     {  0,  0,  1 },   /* left -> delayed left (no change) */
     {  1, -1,  2 },   /* right (left event) -> delayed right */
-    {  2,  0,  3 },   /* left & right (middle press) -> pressed middle */
+    {  9,  0,  3 },   /* left & right (middle press) -> pressed middle */
     {  1,  0,  4 },   /* timeout (left press) -> pressed left */
   },
 /* 2 delayed right */
@@ -112,12 +112,12 @@ static signed char stateTab[11][5][3] = {
     {  3, -3,  0 },   /* nothing (right event) -> ground */
     {  3, -3,  1 },   /* left (right event) -> delayed left (no change) */
     {  0,  0,  2 },   /* right -> delayed right (no change) */
-    {  2,  0,  3 },   /* left & right (middle press) -> pressed middle */
+    {  9,  0,  3 },   /* left & right (middle press) -> pressed middle */
     {  3,  0,  5 },   /* timeout (right press) -> pressed right */
   },
 /* 3 pressed middle */
   {
-    { -2,  0,  0 },   /* nothing (middle release) -> ground */
+    { -9,  0,  0 },   /* nothing (middle release) -> ground */
     {  0,  0,  7 },   /* left -> released right */
     {  0,  0,  6 },   /* right -> released left */
     {  0,  0,  3 },   /* left & right -> pressed middle (no change) */
@@ -141,33 +141,33 @@ static signed char stateTab[11][5][3] = {
   },
 /* 6 released left */
   {
-    { -2,  0,  0 },   /* nothing (middle release) -> ground */
-    { -2,  0,  1 },   /* left (middle release) -> delayed left */
+    { -9,  0,  0 },   /* nothing (middle release) -> ground */
+    { -9,  0,  1 },   /* left (middle release) -> delayed left */
     {  0,  0,  6 },   /* right -> released left (no change) */
     {  1,  0,  8 },   /* left & right (left press) -> repressed left */
     {  0,  0, -1 },   /* timeout N/A */
   },
 /* 7 released right */
   {
-    { -2,  0,  0 },   /* nothing (middle release) -> ground */
+    { -9,  0,  0 },   /* nothing (middle release) -> ground */
     {  0,  0,  7 },   /* left -> released right (no change) */
-    { -2,  0,  2 },   /* right (middle release) -> delayed right */
+    { -9,  0,  2 },   /* right (middle release) -> delayed right */
     {  3,  0,  9 },   /* left & right (right press) -> repressed right */
     {  0,  0, -1 },   /* timeout N/A */
   },
 /* 8 repressed left */
   {
-    { -2, -1,  0 },   /* nothing (middle release, left release) -> ground */
-    { -2,  0,  4 },   /* left (middle release) -> pressed left */
+    { -9, -1,  0 },   /* nothing (middle release, left release) -> ground */
+    { -9,  0,  4 },   /* left (middle release) -> pressed left */
     { -1,  0,  6 },   /* right (left release) -> released left */
     {  0,  0,  8 },   /* left & right -> repressed left (no change) */
     {  0,  0, -1 },   /* timeout N/A */
   },
 /* 9 repressed right */
   {
-    { -2, -3,  0 },   /* nothing (middle release, right release) -> ground */
+    { -9, -3,  0 },   /* nothing (middle release, right release) -> ground */
     { -3,  0,  7 },   /* left (right release) -> released right */
-    { -2,  0,  5 },   /* right (middle release) -> pressed right */
+    { -9,  0,  5 },   /* right (middle release) -> pressed right */
     {  0,  0,  9 },   /* left & right -> repressed right (no change) */
     {  0,  0, -1 },   /* timeout N/A */
   },

これで左右同時押しが正しく動作するようになりました。

最後にxorg.confのEM7のセクションをまとめて掲載しておきます。

Section "InputClass"
	Identifier "Mouse0"
	MatchIsPointer "on"
	MatchProduct "Kensington Expert Mouse"
	Option "ButtonMapping" "1 8 3 4 5 6 7 9 2"
	Option "Emulate3Buttons" "true"
	Option "EmulateWheel" "true"
	Option "EmulateWheelButton" "8"
	Option "EmulateWheelInertia" "20"
	Option "XAxisMapping" "6 7"
	Option "YAxisMapping" "4 5"
EndSection