テンキーを左手入力デバイスとして使う

左手入力デバイスを拡充しようと思い、Keypad SANWA SUPPLY NT-M18UHSV を購入した。 選んだ観点は

  • 00 キーがない。
    • 00 キーを持つテンキーは多いが、たいていは内部的に 0 キーを 2 回押しているだけである。しかもそれを混乱なく実現するために、出力前に同時押下中のキーを上げるコマンドを送出していたりする。その場合、モディファイアキー+00 に機能を割り当てられなかったりするので、左手入力デバイスとして使うには少々問題がある。
  • 極力キー数が多い。
    • キー配置はソフトウェア的に変更できるのでどうでもいい。たとえば 0 キーの幅が広かったり + キーが縦に長かったりするよりは、通常のテンキーとしてはちょっと使いづらい配列でもいいからキー数が多いほうが好ましい。
  • 最低でも 2 キーロールオーバーできる。
    • こればっかりは購入してみないとわからない。試してみた結果、このキーパッドではほとんどのキーの組み合わせで 2 キーまではロールオーバーできる(3 キーになると不具合が発生しはじめる)。
    • 唯一 2 キーロールオーバーできなかったキーが左下の 0 キーで、これは押下持続を全く認識せず押下時に KeyUp イベントも連続して送出してしまう。この挙動は、0 をモディファイアキーとして使わなければ大きな問題とはならない。

このテンキーは、20種類の別個のキーを送出できるという点において、左手入力デバイス用のテンキーとしてはほぼ理想的である。いくつか問題点があるとすれば

  • tab enter return が通常キーボードでの押下と区別されない。
  • 私が利用しているソフトが、テンキーの = キーを通常キーボードの = キーと同一視する(機器からはちゃんと NUMPAD_EQUAL が送出されている)。
  • 私が利用しているソフトが、テンキーの clear を割り当て可能キーとして認識しない。

これらを解決するために、tab clear enter return = の 5 つを別のキーに再割当てする。

  • 私の使用方法だと、テンキーの 1 4 7 をそれぞれ alt shift command (control if Windows) に変更するのでこれらのキーが余る。よってこれらを enter = clear へ再度割り当てることでテンキーとキーボードとを分離する。
  • 私の利用するソフトでは、return キーには単独で押された場合には space と同じ挙動・モディファイアと押された場合には 0 と同じ挙動をしてもらえるとありがたい。よってそのように設定する。
  • このテンキーでは tab キーは非常に押しづらい位置にある。よってこれは弄らない(もしくは nop としてもよい)。

以上を Karabiner を通して設定する。vendorid, productid は Karabiner 内のツールである EventViewer から取得する。

  <devicevendordef>
    <vendorname>SANWASUPPLY</vendorname>
    <vendorid>0xffff</vendorid>
  </devicevendordef>

  <deviceproductdef>
    <productname>NTM18UHSV</productname>
    <productid>0x0200</productid>
  </deviceproductdef>

...
    <item>
      <name>Place Modifiers to Numpad</name>
      <appendix>Numpad 1 to Option (KeyToKey)</appendix>
      <appendix>Numpad 4 to Shift (KeyToKey)</appendix>
      <appendix>Numpad 7 to Command (KeyToKey)</appendix>
      <identifier>private.remap.keypad_modifiers</identifier>
      <autogen>__KeyToKey__ KeyCode::KEYPAD_1, KeyCode::OPTION_L</autogen>
      <autogen>__KeyToKey__ KeyCode::KEYPAD_4, KeyCode::SHIFT_L</autogen>
      <autogen>__KeyToKey__ KeyCode::KEYPAD_7, KeyCode::COMMAND_L</autogen>
    </item>

    <item>
      <name>Change Keypad NT-M18UHSV keys to other keys.</name>
      <appendix>Numpad enter to KEYPAD_1 (KeyToKey)</appendix>
      <appendix>Numpad = to KEYPAD_4 (KeyToKey)</appendix>
      <appendix>Numpad clear to KEYPAD_7 (KeyToKey)</appendix>
      <appendix>Numpad return to space if pressed without modifier, else to 0 (to avoid return/enter key taken unnecessarily by VSTi and Ctrl/Cmd+Space by OS X). (KeyToKey)</appendix>
      <identifier>private.remap.change_tenkey_keys_ntm18uhsv</identifier>
      <device_only>DeviceVendor::SANWASUPPLY, DeviceProduct::NTM18UHSV</device_only>
      <!-- CLEAR can be set to CONTROL_L, and it will expand keypad functionallity, but I won't do so.-->
      <!-- Because it will lose consistency between Windows and Mac.-->
      <autogen> __KeyToKey__ KeyCode::ENTER, KeyCode::KEYPAD_1 </autogen>
      <autogen> __KeyToKey__ KeyCode::KEYPAD_EQUAL, KeyCode::KEYPAD_4 </autogen>
      <autogen> __KeyToKey__ KeyCode::KEYPAD_CLEAR,  KeyCode::KEYPAD_7 </autogen>
      <block>
        <modifier_only>ModifierFlag::NONE</modifier_only>
        <autogen>__KeyToKey__ KeyCode::RETURN, KeyCode::SPACE</autogen>
      </block>
      <autogen> __KeyToKey__ KeyCode::RETURN, KeyCode::0</autogen>
    </item>

00 キー (double 0 key, double O key) の活用方法

以前使っていた iBuffalo BSTK11 において、00 キーをどのように活用していたかをログとして残しておく。

軽く前述したが、00 キーの挙動を Karabiner の EventViewer で観察したところ、たとえば 2 を押しながら 1 を押すと 2↓ 1↓ 1↑ 2↑ という順番で送信されるのだが、00 だけは他の押してあったキーを勝手に離してから 0 を2回押すという挙動をする(2↓ 2↑ 0↓ 0↑ 0↓ 0↑)。これは、テンキーのキーをモディファイアに変えて使っているときに問題となる(モディファイアキー+00 キー を取れない)。 この問題は「前回送られたイベントがモディファイアキーで、かつそのイベントから次のイベントまでが人間には不可能なほど早い」みたいなフィルタをかければ一応は解決可能で、それを実現したのが次の private.xml である。0 を押せば 0 が・00 を押せば 1 が・command+0 を押せば command+0 が・command+00 を押せば(初回は)command+1 が、それぞれ出力される設定となっている。

      <item>
        <identifier>private.remap.double0to1</identifier>
        <name>00 to KEYPAD_1</name>
        <appendix>Change 00 key (quick 0 x2) to KEYPAD_1.</appendix>
        <!-- Filter elapsedtimesincelastreleased_lessthan cannot be used, because it also sends out the first KEYPAD_0. -->
        <!-- Following filter can catch single Mod+00 event, but not Mod+multiple 00 event. -->
        <block>
          <lastsentevent_only>KeyCode::COMMAND_L</lastsentevent_only>
          <elapsedtimesincelastreleased_lessthan>Millisecond::RawValue::10</elapsedtimesincelastreleased_lessthan>
          <autogen>
            __KeyToKey__
            KeyCode::KEYPAD_0,
            KeyCode::VK_NONE,

            Option::KEYTOKEY_DELAYED_ACTION,
            KeyCode::KEYPAD_0, ModifierFlag::COMMAND_L,

            Option::KEYTOKEY_DELAYED_ACTION_CANCELED_BY, KeyCode::KEYPAD_0,
            KeyCode::KEYPAD_1, ModifierFlag::COMMAND_L,
            KeyCode::VK_KEYTOKEY_DELAYED_ACTION_DROP_EVENT,
          </autogen>
        </block>
        <block>
          <autogen>
            __KeyToKey__
            KeyCode::KEYPAD_0,
            KeyCode::VK_NONE,

            Option::KEYTOKEY_DELAYED_ACTION,
            KeyCode::KEYPAD_0,

            Option::KEYTOKEY_DELAYED_ACTION_CANCELED_BY, KeyCode::KEYPAD_0,
            KeyCode::KEYPAD_1,
            KeyCode::VK_KEYTOKEY_DELAYED_ACTION_DROP_EVENT,

            <!-- In the next version, next Option will be added, so remove comment then. -->
            <!-- Option::KEYTOKEY_DELAYED_ACTION_MILLISECONDS, -->
            <!-- Millisecond::RawValue::10, -->
          </autogen>
        </block>
      </item>

しかしこのスクリプトを使っても、モディファイアキーを押しっぱなしにしたまま 00 を繰り返し押すという動作が拾えない。これはそもそもモディファイアキーに変更しているキーが 00 送出時に押し上げられてしまい、その後そのキーは送信されないのでどうやっても解決はできなさそうである。よって モディファイア+00 には、繰り返して実行することがない動作を割り当てるのが重要である(たとえば 拡大・縮小 などを割り当ててはいけない!)。

これと同様の操作を WindowsAutoHotKey で実現する場合は ここ とか参照なんだと思う。しかし設定するよりテンキーを買い換えてしまったほうが早いと思う。