VNC Viewer で戻る・進む・漢字

#はじめに

みなさん VDI してますかー？

このたび CentOS 7 の VNC Viewer で、マウスの戻る・進むボタン、漢字キーを使えるように出来たので、メモを残します。

#背景

個人的に VDI ぽいことをしている私は、VNC に依存した生活をしています。最近わずかに環境を変えたことで、今まで気付かなかった不自由に遭遇しました。

##操作が不自由

マウスを新調したり VNC Viewer を実行する環境を CentOS 7 に変更したりしたところ、少し困ったことになりました。

* マウスの戻る・進むボタンが使えない
* 漢字キーが使えない

まず、「だっせー知らなかったのかよ！」的な感じですが、マウスボタンが無反応でしたｗ　買って届いて挿したら動かないｗ　そして、CentOS 6 ではフツーに使えていた漢字キーも無反応になりましたｗｗ

##原因

VNC では第９ボタン以降が使えません。これは RFB （VNC のプロトコル）の制限で、マウスボタン情報用に１オクテットしか割り当てられていないからです。

加えて TigerVNC （CentOS 7 に同梱されている VNC 実装）は第８ボタン以降が使えません。これは VNC Viewer が利用している FLTK （GUI ツールキット）の制限で、ボタンとしては第１〜３ボタンしか走査していないからです。

> マウスの第１〜３ボタンは左中右ボタン、第４〜７ボタンはホイールの上下左右、第８〜９ボタンが戻る・進むボタンです。

更に、CentOS 7 同梱の VNC Viewer は漢字キーの操作情報を捨てますｗ　なんとｗｗ　これは、TigerVNC 1.3 がキー入力情報をホワイトリストで取捨選択しているからです。そもそも FLTK に「漢字キー」が定義されていないので、リストに入るはずもなく、ポイっとｗ

ちなみに CentOS 6 の TigerVNC 1.1 は、そんな処理してません。最新の 1.8 も、そんな処理してません。

#やったこと

前述の不自由を解消するため、やったことは３つです。

* FLTK に第８〜９ボタンの定義を追加する
* VNC Viewer に第８〜９ボタンイベントを認識させ、戻るキー・進むキーイベントに変換させる
* VNC Viewer に漢字キーを捨てるのを止めさせるｗｗ

##勝手に第８〜９ボタンの定義を追加する

```diff:
diff -urN fltk-1.3.x-r9671/FL/Enumerations.H fltk-1.3.x-r9671-8th9th/FL/Enumerations.H
--- fltk-1.3.x-r9671/FL/Enumerations.H	2012-03-27 01:54:54.000000000 +0900
+++ fltk-1.3.x-r9671-8th9th/FL/Enumerations.H	2017-09-12 22:00:00.000000000 +0900
@@ -433,6 +433,8 @@
 #define FL_BUTTON1	0x01000000	///< Mouse button 1 is pushed
 #define FL_BUTTON2	0x02000000	///< Mouse button 2 is pushed
 #define FL_BUTTON3	0x04000000	///< Mouse button 3 is pushed
+#define FL_BUTTON8	0x08000000	///< Mouse button 8 is pushed
+#define FL_BUTTON9	0x10000000	///< Mouse button 9 is pushed
 #define FL_BUTTONS	0x7f000000	///< Any mouse button is pushed
 #define FL_BUTTON(n)	(0x00800000<<(n)) ///< Mouse button n (n > 0) is pushed
 
diff -urN fltk-1.3.x-r9671/src/Fl_x.cxx fltk-1.3.x-r9671-8th9th/src/Fl_x.cxx
--- fltk-1.3.x-r9671/src/Fl_x.cxx	2012-06-21 17:52:29.000000000 +0900
+++ fltk-1.3.x-r9671-8th9th/src/Fl_x.cxx	2017-09-12 22:00:00.000000000 +0900
@@ -1520,7 +1520,9 @@
 	Fl::e_dx = +1; // Right
 	event = FL_MOUSEWHEEL;
     } else {
-      Fl::e_state |= (FL_BUTTON1 << (xevent.xbutton.button-1));
+      Fl::e_state |= FL_BUTTON(
+        xevent.xbutton.button <= 7 ? xevent.xbutton.button : xevent.xbutton.button - 4
+      );
       event = FL_PUSH;
       checkdouble();
     }
@@ -1570,7 +1572,9 @@
   case ButtonRelease:
     Fl::e_keysym = FL_Button + xevent.xbutton.button;
     set_event_xy();
-    Fl::e_state &= ~(FL_BUTTON1 << (xevent.xbutton.button-1));
+    Fl::e_state &= ~FL_BUTTON(
+      xevent.xbutton.button <= 7 ? xevent.xbutton.button : xevent.xbutton.button - 4
+    );
     if (xevent.xbutton.button == Button4 ||
         xevent.xbutton.button == Button5) return 0;
     event = FL_RELEASE;
```

> 本件と関係ないですが、第４〜５ボタンのリリースイベントを捨てているところ、第６〜７ボタンも捨てないとダメな気がしますが、まぁ、もう古い実装なので。。。
> （第４〜７ボタンは、ボタンイベントではなくホイールイベントとして伝達されます。)

条件演算子はインライン関数にすべきですねぇ。せっかく C++ なのに。

##TigerVNC にパッチ

```diff:戻れ・進め
diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx
index 9fd849f..5b018e6 100644
--- a/vncviewer/Viewport.cxx
+++ b/vncviewer/Viewport.cxx
@@ -422,6 +422,13 @@ int Viewport::handle(int event)
     } 
 
     handlePointerEvent(Point(Fl::event_x() - x(), Fl::event_y() - y()), buttonMask);
+
+    if (Fl::event_buttons() & FL_BUTTON8) {
+      handleKeyEvent(FL_Forward, FL_Forward, "", (event == FL_PUSH));
+    }
+    if (Fl::event_buttons() & FL_BUTTON9) {
+      handleKeyEvent(FL_Back, FL_Back, "", (event == FL_PUSH));
+    }
     return 1;
 
   case FL_FOCUS:
```

```diff:捨てるな漢字
diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx
index 9fd849f..7820877 100644
--- a/vncviewer/Viewport.cxx
+++ b/vncviewer/Viewport.cxx
@@ -737,6 +737,9 @@ rdr::U32 Viewport::translateKeyEvent(int keyCode, int origKe
yCode, const char *k
   case XK_Multi_key:
     // Same for this...
     return XK_Multi_key;
+  case XK_Zenkaku_Hankaku:
+    // pass through to control IM
+    return XK_Zenkaku_Hankaku;
   }
 
   // Unknown special key?
```

#おわりに

RFB を拡張して純粋に第８〜９ボタンの透過伝達を実現することは、今回は見送りました。現状の改修内容だと VNC Server 側は既存のものがそのまま使える利点があります。

というか、最近はマウスボタンが１６個まで認識されるようなので、もしかしたら既に RFB が拡張されているのかも。そこも特に調べませんでした。

そのぶん、バイナリパッチ並のクイックハックに収まっていると思うので、ヨシとします。