Arduino TinyUSBでJPキーボードに対応する（＆マウスの水平ホイールの追加）

# はじめに

この度、マウス/キーボードの操作に[TinyUSB Mouse and Keyboard library](https://github.com/cyborg5/TinyUSB_Mouse_and_Keyboard)を使うマイコンに出会った。今回もJPキーボードに対応したのでここにまとめておく。

https://qiita.com/nak435/items/bbe04300c67c37febb7e


# 1 JPキーボード対応

USBプロトコルスタックをサポートする[Adafruit TinyUSB Library for Arduino](https://github.com/adafruit/Adafruit_TinyUSB_Arduino)は、[HIDキーコード](https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf#page=53)（Usage IDが本来の呼称）を扱うため、キーボードの種類に依存しない。USキーボードとかJPキーボードのキーコードをHIDキーコードに変換しているのは、APIライブラリである[TinyUSB Mouse and Keyboard library](https://github.com/cyborg5/TinyUSB_Mouse_and_Keyboard)だ。

# 1.1 HIDキーコード変換

さっそくコードを眺めてみる。

```cpp:TinyUSB_Mouse_and_Keyboard.cpp（抜粋）
size_t TinyKeyboard_::press(uint8_t k) 
  {
    uint8_t i;
    if (k >= 136) {     // it's a non-printing key (not a modifier)
      k = k - 136;
    } else if (k >= 128) {  // it's a modifier key
      _keyReport.modifiers |= (1<<(k-128));
      k = 0;
    } else {        // it's a printing key
      k = pgm_read_byte(_asciimap + k);
      if (!k) {
        setWriteError();
        return 0;
      }
      if (k & 0x80) {           // it's a capital letter or other character reached with shift
        _keyReport.modifiers |= 0x02; // the left shift modifier
        k &= 0x7F;
      }
    }
    
    // Add k to the key report only if it's not already present
    // and if there is an empty slot.
    if (_keyReport.keys[0] != k && _keyReport.keys[1] != k && 
      _keyReport.keys[2] != k && _keyReport.keys[3] != k &&
      _keyReport.keys[4] != k && _keyReport.keys[5] != k) {
      
      for (i=0; i<6; i++) {
        if (_keyReport.keys[i] == 0x00) {
          _keyReport.keys[i] = k;
          break;
        }
      }
      if (i == 6) {
        setWriteError();
        return 0;
      } 
    }
    sendReport(&_keyReport);
    return 1;
  }
```

最初のif文で引数**k**のコードを3つ振り分けている。コメントにもあるように次のような振り分けである。

|#|条件|意味|キーの例|
|:-:|:-:|:-:|:-:|
|1|136以上のコード<br>(k ≥ 0x88)|非印字文字|矢印<b>↑</b>キーやF1キーなど|
|2|128以上のコード<br>(k ≥ 0x80)|修飾キー|Shift,CtrlやAltキーなど|
|3|それ以外のコード|印字文字|ASCII文字（A,b,&など）|

上表の#1,2のコードが`TinyUSB_Mouse_and_Keyboard.h`に定義されている。

```c:TinyUSB_Mouse_and_Keyboard.h（抜粋）
//  Keyboard codes
//  Note these are different in some respects to the TinyUSB codes but 
//  are compatible with Arduino Keyboard.h API

#define KEY_LEFT_CTRL   0x80
#define KEY_LEFT_SHIFT    0x81
#define KEY_LEFT_ALT    0x82
#define KEY_LEFT_GUI    0x83
#define KEY_RIGHT_CTRL    0x84
#define KEY_RIGHT_SHIFT   0x85
#define KEY_RIGHT_ALT   0x86
#define KEY_RIGHT_GUI   0x87

#define KEY_UP_ARROW    0xDA
#define KEY_DOWN_ARROW    0xD9
#define KEY_LEFT_ARROW    0xD8
#define KEY_RIGHT_ARROW   0xD7
#define KEY_BACKSPACE   0xB2
#define KEY_TAB       0xB3
#define KEY_RETURN      0xB0
#define KEY_ESC       0xB1
#define KEY_INSERT      0xD1
#define KEY_DELETE      0xD4
#define KEY_PAGE_UP     0xD3
#define KEY_PAGE_DOWN   0xD6
#define KEY_HOME      0xD2
#define KEY_END       0xD5
#define KEY_CAPS_LOCK   0xC1
#define KEY_F1        0xC2
#define KEY_F2        0xC3
#define KEY_F3        0xC4
#define KEY_F4        0xC5
#define KEY_F5        0xC6
#define KEY_F6        0xC7
#define KEY_F7        0xC8
#define KEY_F8        0xC9
#define KEY_F9        0xCA
#define KEY_F10       0xCB
#define KEY_F11       0xCC
#define KEY_F12       0xCD
#define KEY_F13       0xF0
#define KEY_F14       0xF1
#define KEY_F15       0xF2
#define KEY_F16       0xF3
#define KEY_F17       0xF4
#define KEY_F18       0xF5
#define KEY_F19       0xF6
#define KEY_F20       0xF7
#define KEY_F21       0xF8
#define KEY_F22       0xF9
#define KEY_F23       0xFA
#define KEY_F24       0xFB
```

**キーボードコード**として、コメントにある通り「HIDキーコードと異なるがArduinoキーボードAPIと互換がある」コードが定義されている。例えば上記の`KEY_F1`は`0xC2`で定義されているがHIDキーコードは`0x3A`、この値の差が確かに136である（<i>0xC2ー0x3A＝0x88</i>）。136を引いてHIDキーコードに変換するということだ。

0x80〜0x87はキーボードの左右にあるCtrl/Shift/Alt/⌘キーに対応しており、[HIDレポートディスクリプタ](https://github.com/adafruit/Adafruit_TinyUSB_Arduino/blob/master/src/class/hid/hid_device.h)のmodifierビットをセットしている。

残るコード値128未満の印字可能文字（ASCII文字）のHIDキーコードへの変換は、変換テーブルを介して行っている。`_asciimap`変換テーブルは `TinyUSB_Mouse_and_Keyboard.cpp` に定義されている。

```c:TinyUSB_Mouse_and_Keyboard.cpp（抜粋）

  #define SHIFT 0x80
  const uint8_t _asciimap[128] =
  {
    0x00,             // NUL
    0x00,             // SOH
    0x00,             // STX
    0x00,             // ETX
    0x00,             // EOT
    0x00,             // ENQ
    0x00,             // ACK  
    0x00,             // BEL
    0x2a,     // BS Backspace
    0x2b,     // TAB  Tab
    0x28,     // LF Enter
    0x00,             // VT 
    0x00,             // FF 
    0x00,             // CR 
    0x00,             // SO 
    0x00,             // SI 
    0x00,             // DEL
    0x00,             // DC1
    0x00,             // DC2
    0x00,             // DC3
    0x00,             // DC4
    0x00,             // NAK
    0x00,             // SYN
    0x00,             // ETB
    0x00,             // CAN
    0x00,             // EM 
    0x00,             // SUB
    0x00,             // ESC
    0x00,             // FS 
    0x00,             // GS 
    0x00,             // RS 
    0x00,             // US 
  
    0x2c,      //  ' '
    0x1e|SHIFT,    // !
    0x34|SHIFT,    // "
    0x20|SHIFT,    // #
    0x21|SHIFT,    // $
    0x22|SHIFT,    // %
    0x24|SHIFT,    // &
    0x34,          // '
    0x26|SHIFT,    // (
    0x27|SHIFT,    // )
    0x25|SHIFT,    // *
    0x2e|SHIFT,    // +
    0x36,          // ,
    0x2d,          // -
    0x37,          // .
    0x38,          // /
    0x27,          // 0
    0x1e,          // 1
    0x1f,          // 2
    0x20,          // 3
    0x21,          // 4
    0x22,          // 5
    0x23,          // 6
    0x24,          // 7
    0x25,          // 8
    0x26,          // 9
    0x33|SHIFT,      // :
    0x33,          // ;
    0x36|SHIFT,      // <
    0x2e,          // =
    0x37|SHIFT,      // >
    0x38|SHIFT,      // ?
    0x1f|SHIFT,      // @
    0x04|SHIFT,      // A
    0x05|SHIFT,      // B
    0x06|SHIFT,      // C
    0x07|SHIFT,      // D
    0x08|SHIFT,      // E
    0x09|SHIFT,      // F
    0x0a|SHIFT,      // G
    0x0b|SHIFT,      // H
    0x0c|SHIFT,      // I
    0x0d|SHIFT,      // J
    0x0e|SHIFT,      // K
    0x0f|SHIFT,      // L
    0x10|SHIFT,      // M
    0x11|SHIFT,      // N
    0x12|SHIFT,      // O
    0x13|SHIFT,      // P
    0x14|SHIFT,      // Q
    0x15|SHIFT,      // R
    0x16|SHIFT,      // S
    0x17|SHIFT,      // T
    0x18|SHIFT,      // U
    0x19|SHIFT,      // V
    0x1a|SHIFT,      // W
    0x1b|SHIFT,      // X
    0x1c|SHIFT,      // Y
    0x1d|SHIFT,      // Z
    0x2f,          // [
    0x31,          // bslash
    0x30,          // ]
    0x23|SHIFT,    // ^
    0x2d|SHIFT,    // _
    0x35,          // `
    0x04,          // a
    0x05,          // b
    0x06,          // c
    0x07,          // d
    0x08,          // e
    0x09,          // f
    0x0a,          // g
    0x0b,          // h
    0x0c,          // i
    0x0d,          // j
    0x0e,          // k
    0x0f,          // l
    0x10,          // m
    0x11,          // n
    0x12,          // o
    0x13,          // p
    0x14,          // q
    0x15,          // r
    0x16,          // s
    0x17,          // t
    0x18,          // u
    0x19,          // v
    0x1a,          // w
    0x1b,          // x
    0x1c,          // y
    0x1d,          // z
    0x2f|SHIFT,    // {
    0x31|SHIFT,    // |
    0x30|SHIFT,    // }
    0x35|SHIFT,    // ~
    0       // DEL
  };
```

記号文字の多くがUSキーボードとJPキーボードでキー位置の割り当てが異なるので、下記のように、ここの定義をJPキーボードに合わせて変更すれば良いはずである。

```diff_cpp:TinyUSB_Mouse_and_Keyboard.cpp（抜粋）

#define SHIFT 0x80
const uint8_t _asciimap[128] =      
{   
    0x00, // NUL    
    0x00, // SOH    
    0x00, // STX    
    0x00, // ETX    
    0x00, // EOT    
    0x00, // ENQ    
    0x00, // ACK    
    0x00, // BEL    
    0x2a, // BS Backspace   
    0x2b, // TAB Tab    
    0x28, // LF Enter   
    0x00, // VT     
    0x00, // FF     
    0x00, // CR     
    0x00, // SO     
    0x00, // SI     
    0x00, // DEL    
    0x00, // DC1    
    0x00, // DC2    
    0x00, // DC3    
    0x00, // DC4    
    0x00, // NAK    
    0x00, // SYN    
    0x00, // ETB    
    0x00, // CAN    
    0x00, // EM     
    0x00, // SUB    
    0x00, // ESC    
    0x00, // FS     
    0x00, // GS     
    0x00, // RS     
    0x00, // US     
    0x2c, // ' '    
    0x1e|SHIFT, // !    
-   0x34|SHIFT, // "    
+   0x1f|SHIFT, // " 34 0x22
    0x20|SHIFT, // #    
    0x21|SHIFT, // $    
    0x22|SHIFT, // %    
-   0x24|SHIFT, // &    
+   0x23|SHIFT, // & 38 0x26
-   0x34, // '  
+   0x24|SHIFT, // ' 39 0x27
-   0x26|SHIFT, // (    
+   0x25|SHIFT, // ( 40 0x28
-   0x27|SHIFT, // )    
+   0x26|SHIFT, // ) 41 0x29
-   0x25|SHIFT, // *    
+   0x34|SHIFT, // * 42 0x2A
-   0x2e|SHIFT, // +    
+   0x33|SHIFT, // + 43 0x2B
    0x36, // ,  
    0x2d, // -  
    0x37, // .  
    0x38, // /  
    0x27, // 0  
    0x1e, // 1  
    0x1f, // 2  
    0x20, // 3  
    0x21, // 4  
    0x22, // 5  
    0x23, // 6  
    0x24, // 7  
    0x25, // 8  
    0x26, // 9  
-   0x33|SHIFT, // :    
+   0x34,       // :
    0x33, // ;  
    0x36|SHIFT, // <    
-   0x2e, // =  
+   0x2d|SHIFT, // =
    0x37|SHIFT, // >    
    0x38|SHIFT, // ?    
-   0x1f|SHIFT, // @    
+   0x2f,       // @
    0x04|SHIFT, // A    
    0x05|SHIFT, // B    
    0x06|SHIFT, // C    
    0x07|SHIFT, // D    
    0x08|SHIFT, // E    
    0x09|SHIFT, // F    
    0x0a|SHIFT, // G    
    0x0b|SHIFT, // H    
    0x0c|SHIFT, // I    
    0x0d|SHIFT, // J    
    0x0e|SHIFT, // K    
    0x0f|SHIFT, // L    
    0x10|SHIFT, // M    
    0x11|SHIFT, // N    
    0x12|SHIFT, // O    
    0x13|SHIFT, // P    
    0x14|SHIFT, // Q    
    0x15|SHIFT, // R    
    0x16|SHIFT, // S    
    0x17|SHIFT, // T    
    0x18|SHIFT, // U    
    0x19|SHIFT, // V    
    0x1a|SHIFT, // W    
    0x1b|SHIFT, // X    
    0x1c|SHIFT, // Y    
    0x1d|SHIFT, // Z    
-   0x2f, // [  
+   0x30, // [
-   0x31, // bslash 
+   0x89, // ¥
-   0x30, // ]  
+   0x32, // ]
-   0x23|SHIFT, // ^    
+   0x2e,       // ^
-   0x2d|SHIFT, // _    
+   0x87|SHIFT, // _
-   0x35,       // `    
+   0x2f|SHIFT, // `
    0x04, // a  
    0x05, // b  
    0x06, // c  
    0x07, // d  
    0x08, // e  
    0x09, // f  
    0x0a, // g  
    0x0b, // h  
    0x0c, // i  
    0x0d, // j  
    0x0e, // k  
    0x0f, // l  
    0x10, // m  
    0x11, // n  
    0x12, // o  
    0x13, // p  
    0x14, // q  
    0x15, // r  
    0x16, // s  
    0x17, // t  
    0x18, // u  
    0x19, // v  
    0x1a, // w  
    0x1b, // x  
    0x1c, // y  
    0x1d, // z  
-   0x2f|SHIFT, // {    
+   0x30|SHIFT, // {
-   0x31|SHIFT, // |    
+   0x89|SHIFT, // |
-   0x30|SHIFT, // }    
+   0x32|SHIFT, // }
-   0x35|SHIFT, // ~    
+   0x2e|SHIFT, // ~
    0 // DEL    
};
```

ところが、この定義では上手く変換できない文字がある。問題となる文字は`¥` `_` `|`の3つ。この3文字のキーコードを見ていただきたい。

```c
  0x89, // ¥
  0x87|SHIFT, // _
  0x89|SHIFT, // |
```

この3文字だけ0x80以上で、Shiftキーを必要とする文字を示す`SHIFT(0x80)`とかぶってしまっているのが原因である。前出の処理コードに従うと「0x80ビットが立っているなら左Shiftキーのmodifierビットをセットして、0x7Fと論理積を取った値をHIDキーコードとしてセットする」ため、期待する文字にならないのだ。

解決には、1文字2バイト構成とする`_asciimap`変換テーブル自体の変更と、それに見合った処理コードの変更が必要である。

`_asciimap`テーブルは、以下のように2バイト構成とし、SHIFTキーの要否を1バイト目、HIDキーコードを2バイト目とする。

## 1.1 HIDキーコード変換テーブルの変更

```diff_cpp
-#define SHIFT 0x80
+#define SHIFT 0x100
-const uint8_t _asciimap[128] =
+const uint16_t _asciimap[128] = {
  0x00, // NUL 0 0x00
  0x00, // SOH 1 0x01
  0x00, // STX 2 0x02
  0x00, // ETX 3 0x03
  0x00, // EOT 4 0x04
  0x00, // ENQ 5 0x05
  0x00, // ACK 6 0x06
  0x00, // BEL 7 0x07
  0x2A, // BS 8 0x08
  0x2B, // TAB 9 0x09
  0x28, // LF 10 0x0A
  0x00, // VT 11 0x0B
  0x00, // FF 12 0x0C
  0x00, // CR 13 0x0D
  0x00, // SO 14 0x0E
  0x00, // SI 15 0x0F
  0x00, // DEL 16 0x10
  0x00, // DC1 17 0x11
  0x00, // DC2 18 0x12
  0x00, // DC3 19 0x13
  0x00, // DC4 20 0x14
  0x00, // NAK 21 0x15
  0x00, // SYN 22 0x16
  0x00, // ETB 23 0x17
  0x00, // CAN 24 0x18
  0x00, // EM 25 0x19
  0x00, // SUB 26 0x1A
  0x00, // ESC 27 0x1B
  0x00, // FS 28 0x1C
  0x00, // GS 29 0x1D
  0x00, // RS 30 0x1E
  0x00, // US 31 0x1F
  0x2C, // Space 32 0x20
  0x1E | SHIFT, // ! 33 0x21
  0x1F | SHIFT, // " 34 0x22
  0x20 | SHIFT, // # 35 0x23
  0x21 | SHIFT, // $ 36 0x24
  0x22 | SHIFT, // % 37 0x25
  0x23 | SHIFT, // & 38 0x26
  0x24 | SHIFT, // ' 39 0x27
  0x25 | SHIFT, // ( 40 0x28
  0x26 | SHIFT, // ) 41 0x29
  0x34 | SHIFT, // * 42 0x2A
  0x33 | SHIFT, // + 43 0x2B
  0x36, // , 44 0x2C
  0x2D, // - 45 0x2D
  0x37, // . 46 0x2E
  0x38, // / 47 0x2F
  0x27, // 0 48 0x30
  0x1E, // 1 49 0x31
  0x1F, // 2 50 0x32
  0x20, // 3 51 0x33
  0x21, // 4 52 0x34
  0x22, // 5 53 0x35
  0x23, // 6 54 0x36
  0x24, // 7 55 0x37
  0x25, // 8 56 0x38
  0x26, // 9 57 0x39
  0x34, // : 58 0x3A
  0x33, // ; 59 0x3B
  0x36 | SHIFT, // < 60 0x3C
  0x2D | SHIFT, // = 61 0x3D
  0x37 | SHIFT, // > 62 0x3E
  0x38 | SHIFT, // ? 63 0x3F
  0x2F, // @ 64 0x40
  0x04 | SHIFT, // A 65 0x41
  0x05 | SHIFT, // B 66 0x42
  0x06 | SHIFT, // C 67 0x43
  0x07 | SHIFT, // D 68 0x44
  0x08 | SHIFT, // E 69 0x45
  0x09 | SHIFT, // F 70 0x46
  0x0A | SHIFT, // G 71 0x47
  0x0B | SHIFT, // H 72 0x48
  0x0C | SHIFT, // I 73 0x49
  0x0D | SHIFT, // J 74 0x4A
  0x0E | SHIFT, // K 75 0x4B
  0x0F | SHIFT, // L 76 0x4C
  0x10 | SHIFT, // M 77 0x4D
  0x11 | SHIFT, // N 78 0x4E
  0x12 | SHIFT, // O 79 0x4F
  0x13 | SHIFT, // P 80 0x50
  0x14 | SHIFT, // Q 81 0x51
  0x15 | SHIFT, // R 82 0x52
  0x16 | SHIFT, // S 83 0x53
  0x17 | SHIFT, // T 84 0x54
  0x18 | SHIFT, // U 85 0x55
  0x19 | SHIFT, // V 86 0x56
  0x1A | SHIFT, // W 87 0x57
  0x1B | SHIFT, // X 88 0x58
  0x1C | SHIFT, // Y 89 0x59
  0x1D | SHIFT, // Z 90 0x5A
  0x30, // [ 91 0x5B
  0x89, // ¥ 92 0x5C
  0x32, // ] 93 0x5D
  0x2E, // ^ 94 0x5E
  0x87 | SHIFT, // _ 95 0x5F
  0x2F | SHIFT, // ` 96 0x60
  0x04, // a 97 0x61
  0x05, // b 98 0x62
  0x06, // c 99 0x63
  0x07, // d 100 0x64
  0x08, // e 101 0x65
  0x09, // f 102 0x66
  0x0A, // g 103 0x67
  0x0B, // h 104 0x68
  0x0C, // i 105 0x69
  0x0D, // j 106 0x6A
  0x0E, // k 107 0x6B
  0x0F, // l 108 0x6C
  0x10, // m 109 0x6D
  0x11, // n 110 0x6E
  0x12, // o 111 0x6F
  0x13, // p 112 0x70
  0x14, // q 113 0x71
  0x15, // r 114 0x72
  0x16, // s 115 0x73
  0x17, // t 116 0x74
  0x18, // u 117 0x75
  0x19, // v 118 0x76
  0x1A, // w 119 0x77
  0x1B, // x 120 0x78
  0x1C, // y 121 0x79
  0x1D, // z 122 0x7A
  0x30 | SHIFT, // { 123 0x7B
  0x89 | SHIFT, // | 124 0x7C
  0x32 | SHIFT, // } 125 0x7D
  0x2E | SHIFT, // ~ 126 0x7E
  0x00, // DEL 127 0x7F  
};
```

## 1.2 press/releaseメソッドの対応

次に処理コード部分を次のように変更する。

```diff_cpp
size_t TinyKeyboard_::press(uint8_t k) {
  uint8_t i;
  if (k >= 136) {     // it's a non-printing key (not a modifier)
    k = k - 136;
  } else if (k >= 128) {  // it's a modifier key
    _keyReport.modifiers |= (1<<(k-128));
    k = 0;
  } else {        // it's a printing key
-   k = pgm_read_byte(_asciimap + k);
+   uint16_t c = _asciimap[k];
-   if (!k) {
+   if (!c) {
      setWriteError();
      return 0;
    }
-   if (k & 0x80) {           // it's a capital letter or other character reached with shift
+   if (c & SHIFT) {          // it's a capital letter or other character reached with shift
      _keyReport.modifiers |= 0x02; // the left shift modifier
-     k &= 0x7F;
    }
+   k = (uint8_t)(c & 0xFF);
  }
  
  // Add k to the key report only if it's not already present
  // and if there is an empty slot.
  if (_keyReport.keys[0] != k && _keyReport.keys[1] != k && 
    _keyReport.keys[2] != k && _keyReport.keys[3] != k &&
    _keyReport.keys[4] != k && _keyReport.keys[5] != k) {
    
    for (i=0; i<6; i++) {
      if (_keyReport.keys[i] == 0x00) {
        _keyReport.keys[i] = k;
        break;
      }
    }
    if (i == 6) {
      setWriteError();
      return 0;
    } 
  }
  sendReport(&_keyReport);
  return 1;
}
```

`TinyKeyboard_::release`メソッドも上記と同様に変更が必要である。

なお、`_asciimap`テーブルが`TinyUSB_Mouse_and_Keyboard.cpp`に定義されているため、USキーボードとJPキーボードを`#ifdef`で分ける共存ができない。これを可能にしたいならば、`_asciimap`テーブルを`TinyUSB_Mouse_and_Keyboard.h`に移し、`#ifdef`でUSキーボード変換テーブルとJPキーボード変換テーブルをコンパイル時に選択するように変更が必要である。JPキーボード専用であれば、このままで良い。

<br>
以上で、JPキーボードに対応する方法は完了。次にマウスの水平ホイール操作を追加する方法を説明する。

# 2. マウスの水平ホイールの追加

そもそもUSBプロトコルスタックをサポートするAdafruit TinyUSB Library for Arduinoには、最初からマウスの水平ホイール（横スクロール）操作は用意されている。しかし、[Arduino標準マウスAPI](https://www.arduino.cc/reference/en/language/functions/usb/mouse/mousemove/)に準拠させたためか、TinyUSB Mouse and Keyboard libraryにはAPIが無い。
このため、`move`メソッドをコピーしてちょっと変更を加えれば使えるようになる。

`TinyMouse_::move`メソッドの4番目の引数**pan**がそれだ。

```diff_c:TinyUSB_Mouse_Keyboard.h
  class TinyMouse_
  {
    private:
      uint8_t _buttons;
      void buttons(uint8_t b);
    public:
      TinyMouse_(void);
      void begin(void);
      void end(void);
      void click(uint8_t b = MOUSE_LEFT);
      void move(signed char x, signed char y, signed char wheel = 0); 
+     void move(signed char x, signed char y, signed char wheel, signed char pan = 0); 
      void press(uint8_t b = MOUSE_LEFT);   // press LEFT by default
      void release(uint8_t b = MOUSE_LEFT); // release LEFT by default
      bool isPressed(uint8_t b = MOUSE_LEFT); // check LEFT by default
  };
```

```diff_cpp:TinyUSB_Mouse_Keyboard.cpp
  void TinyMouse_::move (int8_t x, int8_t y, int8_t wheel) {
    if ( USBDevice.suspended() )  {
      USBDevice.remoteWakeup();
    }
    while(!usb_hid.ready()) delay(1);
    usb_hid.mouseReport(RID_MOUSE,_buttons,x,y,wheel,0);
  }

+ void TinyMouse_::move (signed char x, signed char y, signed char wheel, signed char pan) {
+   if ( USBDevice.suspended() )  {
+     USBDevice.remoteWakeup();
+   }
+   while(!usb_hid.ready()) delay(1);
+   usb_hid.mouseReport(RID_MOUSE,_buttons,x,y,wheel,pan);
+ }
```

上のコードを見れば、使い方は自明だろう。
Arduino標準マウスAPIコンパチといえ上位互換に当たるので最初から用意されていてもおかしくないAPIだ。

以上の対応で快適になったと思われる。


# おわりに

新しいAPIに出会う度に、なんで最初から対応してないの？と思いつつ、いつもJPキーボード対応とマウスの水平ホイール追加をやっている自分がいる。
今回、TinyUSB関連のソースファイルを色々読んだが、HIDレポートディスクリプタ自体の変更が不要だったので、比較的に楽に実現できた。なお、オリジナルのソースファイルに手を加える行為は自己責任とします。

そういえば、Arduino標準APIはまだ対応してないな。使ってないから。


以上