tmytのらくがき

個人の日記レベルです

Surface PenのボタンでSurface Duoのスクリーンショットを撮る

Surface PenはSurface Duoからはキーボードに見えていて、短押しと長押しでそれぞれ特定のスキャンコードを出力するので、Surface Penに設定されているキーレイアウトをカスタムしてPrintScreen*1に割り当てましょう。という話です。

スキャンコードに対応するキーコードを入れ替える

今回は短推しで出力されるキーコード"291"を"SYSRQ"に入れ替えることを目標にします。

Androidに接続されているキーボードは、設定の物理キーボードのレイアウト設定*2で変更できます。しかも、ここのレイアウトはAPKで後から追加できます。じゃぁ作るしかない。

キーボードレイアウトを追加するAPKは、キーキャラクタマップ(KCM)をはじめとしていくつかのファイルが必要です。といっても大したことはないのでAndroidのソースツリーにあるやつをコピペして使うのでほぼ問題ないです。

具体的には、キャラクタマップ、キーレイアウトXML、BroadcastReceiverと、AndroidManifestです。ほかはローカライズで使ってたりするだけなので不要。しいて言えばアイコンぐらいはいるかもしれない。

キャラクタマップの書き方がわからない

書き方は、SDKドキュメントに書いてます。

書いてますがたいして参考になりません。今回みたいな文字を出力しないキーのことはさっぱりわからないのでこのファイルを解釈しているソースを読みます。

このファイルを読んでいくとパーサーの実装が見つかります。

String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
if (keywordToken == "type") {
    mTokenizer->skipDelimiters(WHITESPACE);
    status_t status = parseType();
    if (status) return status;
} else if (keywordToken == "map") {
    mTokenizer->skipDelimiters(WHITESPACE);
    status_t status = parseMap();
    if (status) return status;
} else if (keywordToken == "key") {
    mTokenizer->skipDelimiters(WHITESPACE);
    status_t status = parseKey();
    if (status) return status;
} else {

行頭に現れる値は type, map, key の3種類だそうです。typeの引数は次の6種類があるようです。

  • NUMERIC
  • PREDICTIVE
  • ALPHA
  • FULL
  • SPECIAL_FUNCTION
  • OVERLAY

このtypeOVERLAYにしておくのがいいらしいです。

次に、map。これは次のような構文らしいです。

map key usage {usage code} {key code}
map key {scan code} {key code}

usageが付くパターンは、HIDのUsageを指定するそうですがよくわかりません。ここに

上位の 16 ビットで HID 使用状況ページを、下位の 16 ビットで HID 使用状況 ID を示します。

と書いてあるけれどもそれがどう動くのかはよくわからんやつです。

今回やりたいのはusageが付かない2個目のほう。これを使うと任意のスキャンコードを任意のキーコードに書き換えられます。 たとえばCapslockを左Ctrlに置き換えるとかもこれでできますね。

一番後ろのkey codeはキーコードの名前を指定するとよさそうです。この時指定できる名前は、InputEventLabels.hに定義されてます。

ここを見れば一覧が書いてます。KEYCODE_ってつかないタイプのキーの名前を書けばいい感じですね。

残った、key、これはドキュメントに書いてあるので読んでください。

キャラクタマップを書く

書き方さえわかれば後は簡単。

type OVERLAY
map key 190 SYSRQ

これでおわり。あとはXMLとか用意してAPKにすれば、Surface Penの後ろのボタンでスクリーンショットが取れます。ただ30秒で切断されて反応しなくなるのであんまり役には立たないですね。

今回のソースコードはこちら。

*1:内部的にはSYSRQ

*2:場所は端末によってまちまち、だいたい言語設定の近く