Arduino Leonardoでスキャンコードを指定してキー入力

Arduino Leonardoを使うと簡単に接続されたパソコンにキー入力を行うことができます。
たとえば、```Keyboard.h```をincludeしたプロジェクトで
```Keyboard.print("Hello!");```
と書けば、パソコン側に"Hello!"と入力されます。

このように、"a"とプログラム上で書けばちゃんと"Aのキー"が押下されるのは、利用しているKeyboardプログラムが[^ascii]ASCIIコードから[^scan]スキャンコードに変換してくれているからです。

[^ascii]: 半角のアルファベット大文字・アルファベット小文字・数字・記号の"文字"に割り当てられたコード
[^scan]: キーボード上にあるそれぞれの"キー"に割り当てられたコード。USBキーボードはこいつをパソコンに送っている

この機能は大変便利なのですが問題があって、定義されていないキーは入力できない(Print Screenとか)のと、JIS環境では一部記号の入力がおかしいことです。(変換テーブルがUS配列用のため)

なので、時には直接スキャンコードを使って入力したいことがあるはずです。
ここで書くのはそんな方法。

# Arduino IDEからKeyboardプログラムを取り出す

Arduino IDEに入っているKeyboardプログラムを書き換えるのですが、そのまま書き換えて他のプロジェクトとかに影響あったらダメなので、現在のプロジェクトにコピーしてからそいつを編集します。

Keyboardプログラムは"Keyboard.cpp"と"Keyboard.h"の2つのファイルです。

Macなら"Arduino.app"を右クリックして"パッケージの内容を表示"を選択、あとは"Contents"→"Java"→"libraries"→"Keyboard"→"src"と辿っていけば見つけられるはずです。

Windowsの場合、Arduino公式サイトからダウンロードできるインストール不要のZIPファイルであれば"arduino.exe"と同じ場所に"libraries"フォルダがあるので、その中を"Keyboard"→"src"と辿っていけばあります。
インストーラーとかストアからインストールした場合はパス調べてないのでわからないです。探せばあると思いますが、わからない人は公式からインストール不要版をダウンロードした方がいいと思います。
"Windows ZIP file for non admin install"ってやつです。

2つのファイルを見つけられたら下の画像みたいな感じでプロジェクトのディレクトリにコピーしてください。
<img width="687" alt="スクリーンショット 2018-09-04 23.37.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/242402/aa85a9b7-4142-b985-a91a-6eb7e6b96cfa.png">

# Keyboardプログラムを書き換える

2つのファイルをコピーした状態でinoファイルを開くと、Keyboard.cppとKeyboard.hがタブで選べるようになってるはずです。なので、ここで書き換えて行きましょう。

## Keyboard.cpp
まずはKeyboard.cppです。これを見ると、Keyboard_::press関数とKeyboard_::release関数にある

```c++
	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) {
			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;
		}
	}
```

この部分でASCIIコードからスキャンコードに変換していることは一目瞭然ですね。なので、これを抜いたKeyboard_::pressRaw関数とKeyboard_::releaseRaw関数を書き加えましょう。
あと、それだけだと修飾キーが使えなくなるので、修飾キーを指定する引数mも追加しておきましょう。

```c++
size_t Keyboard_::pressRaw(uint8_t k, uint8_t m) 
{
  uint8_t i;
  // 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;
    } 
  }
  _keyReport.modifiers |= m;
  sendReport(&_keyReport);
  return 1;
}

size_t Keyboard_::releaseRaw(uint8_t k, uint8_t m) 
{
  uint8_t i;
  // Test the key report to see if k is present.  Clear it if it exists.
  // Check all positions in case the key is present more than once (which it shouldn't be)
  for (i=0; i<6; i++) {
    if (0 != k && _keyReport.keys[i] == k) {
      _keyReport.keys[i] = 0x00;
    }
  }

  _keyReport.modifiers &= ~m;
  sendReport(&_keyReport);
  return 1;
}
```

あとはKeyboard_::writeRaw関数もあったら便利ですね。付け加えておきましょう。

```c++
size_t Keyboard_::writeRaw(uint8_t c, uint8_t m)
{  
  uint8_t p = pressRaw(c, m);  // Keydown
  releaseRaw(c, m);            // Keyup
  return p;              // just return the result of press() since release() almost always returns 1
}
```


## Keyboard.h

スキャンコードでキー入力を行う関数は実装できましたが、ヘッダーファイルに追加した関数を記述してないのでまだ呼び出せません。なので、Keyboard.hの下の方のpublic:ってところに新しく定義した関数を追加しておきましょう。

```c++:Keyboard.h
// --省略--

class Keyboard_ : public Print
{
private:
  KeyReport _keyReport;
  void sendReport(KeyReport* keys);
public:
  Keyboard_(void);
  void begin(void);
  void end(void);
  size_t write(uint8_t k);
  size_t writeRaw(uint8_t k, uint8_t m = 0);
  size_t press(uint8_t k);
  size_t pressRaw(uint8_t k, uint8_t m = 0);
  size_t release(uint8_t k);
  size_t releaseRaw(uint8_t k, uint8_t m = 0);
  void releaseAll(void);
};
extern Keyboard_ Keyboard;

#endif
#endif
```

あと、そのままだと修飾キーの指定が面倒なので、#defineが並んでる一番下あたりに修飾キー用のマクロを定義しておきます。

```c++
#define MODIFIER_LEFT_CTRL   0b00000001
#define MODIFIER_LEFT_SHIFT    0b00000010
#define MODIFIER_LEFT_ALT    0b00000100
#define MODIFIER_LEFT_GUI    0b00001000
#define MODIFIER_RIGHT_CTRL    0b00010000
#define MODIFIER_RIGHT_SHIFT   0b00100000
#define MODIFIER_RIGHT_ALT   0b01000000
#define MODIFIER_RIGHT_GUI   0b10000000
```

# 使い方

includeは
```#include "Keyboard.h"```
で大丈夫です。

元々定義してある関数は変更してないので、アルファベット入力する際はそっち使えばいいです。

[USBキーボードのキーコード](http://www2d.biglobe.ne.jp/~msyk/keyboard/layout/usbkeycode.html)
このサイトでスキャンコードが調べられるので、調べたスキャンコードをKeyboard.writeRaw関数、もしくはKeyboard.pressRaw関数とKeyboard.releaseRawに渡します。
例えば、Print Screenは0x46なので、
```Keyboard.writeRaw(0x46);```
で入力できます。
修飾キーは第2引数に指定するので、例えばAlt+Print Screenを入力したいときは
```Keyboard.writeRaw(0x46, MODIFIER_LEFT_ALT);```
とすればいいでしょう。
Ctrl+Alt+Deleteなら
```Keyboard.writeRaw(0x4c, MODIFIER_LEFT_CTRL | MODIFIER_LEFT_ALT);```
ですね。
```Keyboard.write('\\');```
ではJIS環境で入力できなかったバックスラッシュも、
```Keyboard.writeRaw(0x89);```
と書けば入力できます。
押しっぱなしにしたいときはKeyboard.pressRaw、それを解除したいときはKeyboard.releaseRawであり、キーの指定方法はKeyboard.writeRawと同じです。