VIA JSON Building Tool （VIA 対応お助けツール）を作ってみた

# はじめに

自作キーボードのキーマップを変更する方法としては、主に以下のような手段がありました

* `keymap.c` を直接編集する
* QMK Configurator を使用する

そこに [VIA][via] という新しいツールが追加されました

今回、自作キーボードの VIA 対応を手助けするツールを作ったので、この記事で使用方法などを説明していきます
また、その特性上、自作キーボード設計者向けの内容となっておりますので、ご了承ください

# この記事とツールの対象者

この記事とツールは以下のような人たちに向けて作られています

* ファームウェア対応したけど、 JSON の作り方わからん！
* JSON わからなくもないけど、手打ちするの面倒すぎるんだが？

よって、 VIA についてある程度理解している人向けになります（なので、 VIA についての詳しい説明は割愛します）
~~また、このツールで作成した JSON が VIA で問題なく読み込めることは確認していますが、<font color="red">キーボードを使用した実機検証はまだ行っていません</font>~~

~~申し訳ありません~~

<font color="green">**実機でも問題なく動作することを確認しました！ (2020/08/26 追記)**</font>

以上のことを踏まえて、以下、お読みください

# VIA とは

自作キーボード界隈で話題に上がっている、キーマップを簡単に変更・反映させることができるツールです

詳しくは [公式ページ][via] をご覧ください
また、使い方については [サリチル酸][salicylic] 氏が詳しい解説をしておりますので、そちらもご覧ください

* [（初心者編）VIAを使ってキーマップを書き換えよう][salicylic_via_man]
* [（設計者向け）VIA対応のファームウェアを作ろう][salicylic_via_sup]


# VIA JSON Building Tool

[VIA JSON Building Tool][vjb]

今回作成した、 VIA 対応で必要な JSON の作成を助けるツール群です
ソースコードは [こちら][vjb_repo]

## 概要

**VIA JSON Building Tool** は主に3つの画面から構成されています

### Home 画面

VIA や各ツールの簡単な説明や注意事項などが書かれている画面

![vjb_home]

### Builder 画面 (VIA JSON Builder)

VIA 用 JSON を作成するための画面

![vjb_builder]

### Converter 画面 (Keymap Converter)

VIA 用 JSON に使用するキーマップを作成するための画面
VIA で使用されるキーマップは [Keyboard Layout Editor][kle] （以下、 KLE）フォーマットの JSON です
つまり、 KLE 用の JSON を作成するツールとなります

![vjb_converter]

## 使い方

**VIA JSON Building Tool** では、 QMK Firmware の各ファイルを使って各 JSON を作成します
Home 画面の注意事項にも書いてありますが、<font color="red">このツールは既にあるファームウェアを VIA 用に変換するものではないので、ファームウェア側で VIA 対応の準備をしてから使用してください</font>

### VIA JSON Builder

![vjb_builder]

#### 必要なファイル

* `config.h`
* KLE フォーマットのキーマップ JSON ファイル

キーマップ JSON は、 KLE で作成したものや、後述の **Keymap Converter** で作成したものを使用できます
ただし、 KLE で編集したものを使用する場合は、 <font color="red">Raw タブに表示されているものではなく、ダウンロードしたものを使用してください</font>
Raw タブの JSON は、ラベルが `"` で囲まれていない場合があり、正しく読み込めないためです（ダウンロードしたものはラベルが `"` で囲まれているため、正しく読み込むことができます）

#### 作成手順

1. 上部のドラッグ&ドロップエリアに `config.h` ファイルをドラッグ&ドロップしアップロード
2. 上部のドラッグ&ドロップエリアにキーマップ JSON ファイルをドラッグ&ドロップしアップロード
3. Lighting を選択（詳細は後述）
4. 必要であれば、 Labels を入力（書き方の詳細等は [公式ドキュメント][via_doc_layouts] を確認してください）
5. ダウンロードボタン or クリップボードにコピーボタンで JSON を取得

クリップボードにコピーした場合は、ローカルファイルにペーストして保存してください

##### Lighting 項目について

詳しい説明は [VIA の公式ドキュメント][via_doc_lighting] をご覧ください

この項目は、キーボードの LED について設定します
主に使うのは以下の4種類でしょう

|値|説明|
|:--|:--|
|none|LED なし|
|qmk_backlight|バックライト（主に PWM などで制御する単色 LED）を使用可にする|
|qmk_rgblight|RGB LED （主に SK6812MINI など制御 IC が載っているフルカラー LED）を使用可にする|
|qmk_backlight_rgblight|RGB LED とバックライトを使用可にする|

VIA 対応するキーボードに合わせて選択してください

### Keymap Converter

![vjb_converter]

#### 必要なファイル

* `<keyboard>.h`
* `info.json`

`<keyboard>.h` は、レイアウトマクロが定義されているファイルです
キーボードによっては `rev1.h` などに定義されている場合があります
レイアウトマクロとは、以下のようなものです

```cpp
#define LAYOUT( \
  k00, k01, k02, \
  k10, k11, k12, \
  k20, k21, k22, \
  ) \
  { \
    { k00, k01, k02 }, \
    { k10, k11, k12 }, \
    { k20, k21, k22 }, \
  }
```

`info.json` については、なくても問題ありません
ただし、その場合はキーレイアウトのデザインとなるデータがないため、 KLE や VIA で表示したとき見栄えが悪くなります
`info.json` がない場合は、 KLE で頑張って見た目を整えてください

#### 作成手順

1. 上部のドラッグ&ドロップエリアに `<keyboard>.h` ファイルをドラッグ&ドロップしアップロード
2. 上部のドラッグ&ドロップエリアに `info.json` ファイルをドラッグ&ドロップしアップロード
3. コンバートボタンを押して JSON を作成
4. ダウンロードボタン or クリップボードにコピーボタンで JSON を取得

クリップボードにコピーした場合は、 KLE の Raw タブや、 **VIA JSON Builder** の Keymap テキストエリアに直接ペーストすることで、円滑に作業ができると思います

#### おまけの使い方

**Keymap Converter** では、 `<keyboard>.h` と `info.json` を使用して、単純な KLE 用の JSON を作成することもできます
手順は基本と同じですが、ラジオボタンを `VIA` から `Plane KLE` に変更してコンバートしてください

## 処理の詳細

以下は、 VIA に必要な情報やその場所、それをどうやって抽出しているのかを説明します
興味がない人は読み飛ばしてください

### VIA JSON Builder

VIA では以下の項目が必要となります

* `name`
* `vendorId`
* `productId`
* `lighting`
* `matrix`
* `layouts`

更に、 `matrix` と `layouts` は以下の項目で構成されています

`matrix`

* `rows`
* `cols`

`layouts`

* `labels`
* `keymap`

`lighting` と `layouts` 以外は QMK の `config.h` から取得することができ、以下のような対応があります

|VIA 項目|QMK (`config.h`) 項目|
|:--|:--|
|`name`|`PRODUCT`|
|`vendorId`|`VENDOR_ID`|
|`productId`|`PRODUCT_ID`|
|`rows`|`MATRIX_ROWS`|
|`cols`|`MATRIX_COLS`|

**VIA JSON Builder** では、 `config.h` がアップロードされたタイミングで内容を解析し、以上の情報を抽出、設定しています

`keymap` については、 KLE フォーマットの JSON です

### Keymap Converter

VIA では、キーマトリクスの定義として、 KLE の左上のレジェンドの値を使用しています
レジェンドに `row,col` の形で入力することでキーマトリクスを構成します（e.g. `["0,0","0,1"...],...`）

これを機械的に採番するため、 `<keyboard>.h` のレイアウトマクロを使用しています
レイアウトマクロでは、物理レイアウトと論理レイアウトが定義されています（私が勝手にこう呼んでいます）
**Keymap Converter** ではコンバートボタンが押されたタイミングでレイアウトマクロを解析し、論理レイアウトの順番で `row` と `col` を採番し、物理レイアウトに合わせて整列させています
また、 `info.json` があった場合、 KLE フォーマットに合わせて、キーの幅、高さ、X位置、Y位置を計算し挿入しています
これによって、 KLE で表示したとき、物理レイアウトがおおよそ正しく再現されるようになります

# 使用技術について

## 使用フレームワークについて

**VIA JSON Building Tool** は [Blazor WebAssembly][blazor] (C#) で動作しています
また、 [GitHub Pages][gh_pages] を使用して公開しています
Blazor についてはほぼ初めてなので、ベストプラクティスから外れた実装もあるかと思いますが、温かい目で見守っていただけると幸いです

Blazor アプリケーションを GitHub Pages で公開・実行するにはいくつかのテクニックが必要ですが、それを簡単にする拡張テンプレートが公開されており、今回はそれを利用しています

それが [Blazor GitHub Pages SPA][blazor_ghp] です
[GitHub リポジトリ][blazor_ghp_repo] にソースコードと詳しい使い方が公開されています

この拡張テンプレートには、 GitHub Pages で Blazor アプリケーションを動作させるための各種テクニックや、 GitHub Pages をデプロイするための GitHub Actions が既に含まれた状態でプロジェクトが作成されるので、それらを気にする必要なくコードの作成のみに集中できます

そのほか、様々な NuGet パッケージを使用しているので、以下に挙げておきます

* [BlazorDownloadFile][bdf] ([GitHub][bdf_repo])
* [BlazorInputFile][bif] ([GitHub][bif_repo])
* [Blazored.SessionStorage][bss] ([GitHub][bss_repo])
* [CurrieTechnologies.Razor.Clipboard][ctrc] ([GitHub][ctrc_repo])
* [Utf8Json][u8j] ([GitHub][u8j_repo])

## `*.h` の解析について

正規表現で解析しています
C# の [Roslyn][roslyn] のような構文解析エンジンがあればよかったのですが、見つけることができなかったため、正規表現の力業でごり押ししました

# おわりに

いかがでしたでしょうか
つたないところもあったかと思いますが、 **VIA JSON Building Tool** が VIA 対応の助けになれば嬉しく思います
また、説明で分かりにくいところや、バグを発見したら、コメント、編集リクエスト、 GitHub の issue などでお知らせください

[vjb]:https://koktoh.github.io/ViaJsonBuilder/
[vjb_repo]:https://github.com/koktoh/ViaJsonBuilder
[via]:https://caniusevia.com/
[via_doc_lighting]:https://caniusevia.com/docs/specification#lighting
[via_doc_layouts]:https://caniusevia.com/docs/optional#layouts
[kle]:http://www.keyboard-layout-editor.com/
[salicylic]:https://twitter.com/Salicylic_acid3
[salicylic_via_man]:https://salicylic-acid3.hatenablog.com/entry/via-manual
[salicylic_via_sup]:https://salicylic-acid3.hatenablog.com/entry/via-support

[vjb_home]:https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553901/06973e9b-88d4-d9e9-9f51-9f03c48c9566.png "ViaJsonBuilder Home"
[vjb_builder]:https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553901/0e5aafe3-6c06-b7a0-41c5-bc19d11518e8.png "ViaJsonBuilder Builder"
[vjb_converter]:https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553901/bf2d7fa5-8b76-61e6-566d-fab8188e993d.png "ViaJsonBuilder Converter"

[blazor]:https://docs.microsoft.com/ja-jp/aspnet/core/blazor/?view=aspnetcore-3.1
[gh_pages]:https://docs.github.com/ja/github/working-with-github-pages/about-github-pages
[blazor_ghp]:https://marketplace.visualstudio.com/items?itemName=pierre3.BlazorWASMGitHubPages
[blazor_ghp_repo]:https://github.com/pierre3/blazor-wasm-github-pages-template
[bdf]:https://www.nuget.org/packages/BlazorDownloadFile/
[bdf_repo]:https://github.com/arivera12/BlazorDownloadFile
[bss]:https://www.nuget.org/packages/Blazored.SessionStorage/
[bss_repo]:https://github.com/blazored/SessionStorage
[bif]:https://www.nuget.org/packages/BlazorInputFile/
[bif_repo]:https://github.com/SteveSandersonMS/BlazorInputFile
[ctrc]:https://www.nuget.org/packages/CurrieTechnologies.Razor.Clipboard/
[ctrc_repo]:https://github.com/Basaingeal/Razor.Clipboard
[u8j]:https://www.nuget.org/packages/Utf8Json/
[u8j_repo]:https://github.com/neuecc/Utf8Json/
[roslyn]:https://docs.microsoft.com/ja-jp/visualstudio/code-quality/roslyn-analyzers-overview?view=vs-2019