パソコンなど

パソコンの話をします。

WacomのペンタブをX上でつかう

タブレットバイス

タブレットバイス(いわゆるペンタブ)は、絵や文字を描くときに便利な入力デバイスである。マウスやトラックポイントタッチパッドに比べて遥かに自然に線などを描くことができる。

X上で使う上での問題点

X上では、タブレットバイスは、スクリーン全体にマッピングされる。スクリーンののアスペクト比と、タブレットバイスの物理的なアスペクト比がほぼ一致している場合はそこまで問題にならない。しかし、マルチモニターなどによって、スクリーンとタブレットバイスアスペクト比が大きく異なる場合、普通に書き込むと描いた内容が引き伸ばされる。また、スクリーン上の小さな領域に描画したい場合、タブレットバイスの非常に小さな領域に描く必要があり、物理的なセンサー面の殆どが無駄になる。

私のデスクトップ環境は、2560x1600のモニターの右側に3840x2160のモニターを並べたマルチモニターとなっており、スクリーン全体は6400x2160の大きさである。一方使用しているタブレットバイスはセンサー面のサイズが21600x13500の16:10のアスペクト比となっている。スクリーン全体のアスペクト比を縦10に変換すると、29.63:10となり、倍弱ほど横長なスクリーン領域となる。そのため、普通にタブレットバイスマッピングして描くとかなり横に伸びてしまう。

私のデスクトップ環境

普通のマッピングで描いた様子、横にかなり引き伸ばされる。

目的の状態

横長のウィンドウへのマッピング

縦長のウィンドウへのマッピング

正しくマッピングされたウィンドウに描いた様子

対処法

  • xinputコマンドを用いることで、タブレットバイスマッピングされるスクリーン上の領域を変更できる。
    • xinput set-prop <device id> Coordinate\ Transformation\ Matrix <affine transformation matrix>
  • <device id>で、タブレットが割り当てられているIDを選択する。
  • <affine transformation matrix>は、マッピングされる領域を変換するアフィン行列を展開した9つのfloatの値を入力する。

device idを調べる

  • xinput listで繋がっているデバイスのリストが得られる。
    • ここでは、Wacom One by Wacom M Pen stylusがそれで、idは11であることがわかる。
$ xinput list
⎡ Virtual core pointer                        id=2    [master pointer  (3)]
⎜   ↳ Virtual core XTEST pointer                id=4    [slave  pointer  (2)]
⎜   ↳ ELAN0678:00 04F3:3195 Mouse               id=14   [slave  pointer  (2)]
⎜   ↳ ELAN0678:00 04F3:3195 Touchpad            id=15   [slave  pointer  (2)]
⎜   ↳ TPPS/2 Elan TrackPoint                    id=17   [slave  pointer  (2)]
⎜   ↳ Wacom One by Wacom M Pen stylus           id=11   [slave  pointer  (2)]
⎜   ↳ Wacom One by Wacom M Pen eraser           id=19   [slave  pointer  (2)]
⎣ Virtual core keyboard                       id=3    [master keyboard (2)]
    ↳ Virtual core XTEST keyboard                 id=5    [slave  keyboard (3)]
    ↳ Power Button                                id=6    [slave  keyboard (3)]
    ↳ Video Bus                                   id=7    [slave  keyboard (3)]
    ↳ Power Button                                id=8    [slave  keyboard (3)]
    ↳ Sleep Button                                id=9    [slave  keyboard (3)]
    ↳ Integrated Camera: Integrated C             id=12   [slave  keyboard (3)]
    ↳ Integrated Camera: Integrated I             id=13   [slave  keyboard (3)]
    ↳ AT Translated Set 2 keyboard                id=16   [slave  keyboard (3)]
    ↳ ThinkPad Extra Buttons                      id=18   [slave  keyboard (3)]
    ↳ Solid State System Co.,Ltd. LCS USB Audio   id=10   [slave  keyboard (3)]
$
  • xsetwacom --list devicesWacomのデバイスに限定して調べられる。
    • 前のものと同じであることがわかる。
$ xsetwacom --list devicesp
Wacom One by Wacom M Pen stylus     id: 11  type: STYLUS
Wacom One by Wacom M Pen eraser     id: 19  type: ERASER
$

アフィン変換行列を求める

  • xinputのCoordinate Transformation Matrixの引数にはアフィン変換行列を与える。
  • スクリーン全体にマッピングされた状態から、目的の状態への変換行列を求める必要がある。
  • 具体的には、原点(左上)を中心に、ウィンドウサイズに合わせてスケーリングを行う変換行列と、ウィンドウの左上に合わせて平行移動を行う変換行列の積を求める。

スケーリング

平行移動

  • ウィンドウの左上の座標がスクリーン上でどこになるかを求める。
    • ウィンドウの位置
      • xdotool getwindowgeometry <window id>
    • スクリーンのサイズ
      • xdpyinfo
      • xrandr

実装

  • 上に示したようなコマンドを実行して結果をパースし、アフィン行列を計算、xinputでペンタブを設定するプログラムを実装した。

github.com

付録(それぞれのコマンドの出力)

  • $で始まる行はコマンド入力である。
  • ペンタブのサイズ: xinput watch-props <device id>
$ xinput watch-props 11
Device 'Wacom One by Wacom M Pen stylus':
    Device Enabled (163):   1
    Coordinate Transformation Matrix (165): 1.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 1.000000
    Device Accel Profile (290): 0
    Device Accel Constant Deceleration (291):   1.000000
    Device Accel Adaptive Deceleration (292):   1.000000
    Device Accel Velocity Scaling (293):    10.000000
    Device Node (287):  "/dev/input/event13"
    Wacom Tablet Area (300):    0, 0, 21600, 13500
    Wacom Rotation (301):   0
    Wacom Pressurecurve (302):  0, 0, 100, 100
    Wacom Serial IDs (303): 891, 0, 2, 0, 0
    Wacom Serial ID binding (304):  0
    Wacom Proximity Threshold (305):    30
    Wacom Pressure Threshold (306): 26
    Wacom Sample and Suppress (307):    2, 4
    Wacom Enable Touch (308):   0
    Wacom Hover Click (309):    1
    Wacom Enable Touch Gesture (310):   0
    Wacom Touch Gesture Parameters (311):   0, 0, 250
    Wacom Tool Type (312):  "STYLUS" (289)
    Wacom button action 0 (313):    1572865
    Wacom button action 1 (314):    1572866
    Wacom button action 2 (315):    1572867
    Wacom button action 3 (316):    1572872
    Wacom Button Actions (317): "Wacom button action 0" (313), "Wacom button action 1" (314), "Wacom button action 2" (315), "None" (0), "None" (0), "None" (0), "None" (0), "Wacom button action 3" (316)
    Wacom Pressure Recalibration (318): 1
    Wacom Panscroll Threshold (319):    1300
    Device Product ID (288):    1386, 891
    Wacom Debug Levels (320):   0, 0
$
  • ペンタブのサイズ: xsetwacom --get <device name> Area
$ xsetwacom --get "Wacom One By Wacom M Pen stylus" Area
0 0 21600 13500
$
  • ウィンドウのジオメトリ: xdotool getwindowgeometry <window id>
  • ウィンドウのID: xdotool selectwindow
$ xdotool getwindowgeometry $(xdotool selectwindow)
Window 4194313
  Position: 1280,0 (screen: 0)
  Geometry: 1280x1600
$
  • スクリーンのサイズ: xdpyinfo
$ xpyinfo | grep dimensions
  dimensions:    6400x2160 pixels (1684x568 millimeters)
$
  • スクリーンのサイズ: xrandr
$ xrandr | grep Screen
Screen 0: minimum 320 x 200, current 6400 x 2160, maximum 16384 x 16384
$

ThinkPad X13 Gen 3 (AMD) 良かったよの話

  • 少し前にパソコンを新調した。
  • 結構良いマシンだと思ったのでレビューする。

主なカスタマイズ

  • CPU: Ryzen7 PRO 6850U
  • RAM: 32GB
  • Battery: 54Wh (4 cells)
  • Display: 2560x1600
  • Keyboard: English, backlight
  • SSD: WD-black 1TB (replaced)
  • WLAN: Qaulcomm QCNFA765

購入の決め手

  • ThinkPadトラックポイントキーボードを1年ほど前から使っており、気に入っていた。
  • 2022年末におけるAMDの最新のモバイルプロセッサを積んでおり、そこそこ処理が速い。
    • TDPは28Wしかないのにも関わらず、200W消費するi9 9900K(自宅のデスクトップパソコン)とほぼ同じ性能らしい。
  • intelの同じグレードの製品であるi7 1280Pと比べた時、グラフィック以外の処理性能はほぼ互角。
    • グラフィック性能に優れ、その上で電力効率も高い。
    • また、価格も数万円安い。

筐体、キーボード、排熱、バッテリーなど

  • MacBookPro 2018 13inchから乗り換えたので、結構分厚いなという印象を受けた。
  • ThinkPadの外付けキーボードに比べるとキーストロークは浅い。レイアウトは同じで、キーボードとして一般的な(失敗できる)価格なので、ThinkPadが気になる人は試してみると良いだろう。
  • バッテリーについて、Zoomで常に2560x1600の画面共有を映しながらの100分の講義には余裕で足りるが、2コマ目の半分ぐらいで尽きる。
    • この状態での消費電力(powertopで確認)は16W程度だった。
  • 排熱機構は底面から吸気して右手側に排気する一般的なもの。筐体は樹脂製なのでパームレストまで熱くなることはないが、高負荷時キーボードの横の部分に触れるとそこそこ熱くなっているのを感じる。
    • 参考までにCPUフルロード+Google Chrome上のYouTubeで1080pの動画を再生しているときの消費電力(powertopで確認)は最大30W程度だった。
    • ベンチマークサイトによると、45W程度まではいくらしいが、CPUとGPUの両方に高負荷がかかるタスクはそんなに多くないだろうと思う。
  • マイクは普通。スピーカーの音質はとても良いとは言えないが、これはMacBookを除くほとんどのノートパソコンでもそうだろう。

ソフトウェア

  • Linuxを入れて使う場合、バージョン1.25より前のBIOSにはバグがあるらしく、アップデートが必要らしい。
  • Debian GNU/Linux 11において、ネットワークアダプタとマイクのファームウェアがなかった。
    • 12のnon-freeにはあったが、インストール時に使えなかったので、RJ-45のコネクタがあるに越したことはないだろう。
  • WindowsUbuntuは割と正常に動作していたと思う。(あまり試していないけど)

総評

  • キーボードとトラックポイントが使いやすく、もうこれなしでは生きていけないかもしれない。
  • CPUの処理性能がほぼ互角で、マルチモニターにしてもグラフィック性能に余裕があるので、前に使っていたノートパソコンだけでなく、デスクトップパソコンの役目も置き換えてしまった。
  • ThinkPadRyzen 6850Uを搭載できるものはX13とT14sしかないので、グラフィックと電力効率を重視するユーザーにとって、これらはX1以上に有力な選択肢かもしれない。
  • キーボードにこだわりがないなら、より大容量なバッテリーを搭載したLG gramや、色々な意味で高水準なMacBookProも候補になると思う。

自作ウィンドウマネージャとほげWM

私のデスクトップ環境(左上側にFHD、右側に4Kモニタ)

  • Linuxを快適に使うために、X上のウィンドウマネージャを自作した。
  • 生のX、または自作ウィンドウマネージャに関する日本語の情報はほとんどないので、これを書こうと思う。

1. ウィンドウマネージャとHOGEWM

1.1. ウィンドウマネージャとは

 広く用いられているGUIデスクトップ環境は、ソフトウェアが一定の画面領域、ウィンドウを操作することで成り立っている。そのウィンドウの位置や大きさを変更したり、入力するウィンドウを選択したりするのがウィンドウマネージャである。

1.2. WMを自作すべきな理由

  • 自分で設計したキーバインドで、自分で設計した動作をさせることができるため、非常に快適に操作でき、生産性が高まる。
  • パソコンがより好きになる。

1.3. HOGEWMとは

1.3.1. リポジトリ

1.3.2. 主な特徴

  • ほぼ全てのウィンドウ操作をキーボードのみを用いて行える。
  • マルチモニタ環境での使用を前提とした設計。

1.3.3. 主な機能

私の環境ではMod1はAltキー

バインド 動作
Ctrl-Mod1-i ウィンドウの選択
Ctrl-Mod1-n 選択したウィンドウを隣のモニターに移動
Ctrl-Mod1-s 全てのウィンドウをモニターごとに入れ替え
Ctrl-Mod1-m 選択したウィンドウを最大化
Ctrl-Mod1-h,j,k,l 左下上右寄せで選択したウィンドウを半分にする
Ctrl-Mod1-1 urxvtを起動(ソースコードから変更可)
Ctrl-Mod1-2 emacsを起動(ソースコードから変更可)
Ctrl-Mod1-3 google-chromeを起動(ソースコードから変更可)
Ctrl-Mod1-t 全てのウィンドウをタイル配置
Mod1-1,2,3,4 バーチャルスクリーン1-4に切り替え
Ctrl-Mod1-a,d 隣のバーチャルスクリーンに選択したウィンドウを移動
Ctrl-Mod1-r 選択したウィンドウを最前面に配置
Print 画面全体のSSを$HOME/screenshots/に保存
Ctrl-Print 選択したウィンドウのSSを$HOME/screenshots/に保存
Mod1-leftdrag ウィンドウの移動
Mod1-rightdrag ウィンドウのリサイズ

2. 開発に入る前に

  • 開発に入る前に、開発環境とその使い方を知る必要がある。

2.1. 使用している主なソフトウェア

2.2. Xが起動するざっくりとした流れ

  1. startxを実行
  2. $HOME/.xinitrcに書かれたコマンドが順に呼び出されるので、そこにWMやターミナルエミュレータを入れておく。
  3. 最後に実行するものは、処理が戻らないように末尾に&をつけない。
  4. 以下の.xinitrcでは、urxvtが最後に起動する。そのため、urxvtをexitなどで終了するとXorgが落ちる。
#!/bin/sh

xset +fp $HOME/.local/share/fonts/
xrdb $HOME/.Xdefaults
xmodmap $HOME/.Xmodmap

xset r rate 200 40
setxkbmap -option ctrl:nocaps -option terminate:ctrl_alt_bksp

$HOME/hogewm/hogewm 2> /dev/null &
emacs &
urxvt -geometry 80x35-64-0

2.3. WMを起動する

  • 私の環境では、上記の.xinitrcの下から3行目でWMを起動している。あなたが作っているWMを起動するコマンドをこれと置き換えればhogewmの代わりにそれが起動する。

  • ただし、WMを2つ以上同時に起動すべきではない。なぜならキーバインドが衝突して後から起動した方が動かなくなるからである。

  • WMを.xinitrcの末尾に置くべきではない。末尾においた場合、WMを終了したときにXサーバーが終了してしまう。
    • 開発初期のWMというのは不安定で頻繁にクラッシュするし、開発中はWMを幾度となく再起動するだろう。これでは都合が悪い。

3. 自作する上でのTIPS

  • ここまでで、WMを実行することまではできるようになった。

3.1. 他のWMを動かして、読もう

  • 自分が書こうとしている言語で書かれたWMの動作を観察して、ソースコードを読むのは必須に近い。
  • GitHubでは言語を指定した検索もできるので、window managerとかで調べれば良いプロジェクトがたくさん見つかる。
  • 推奨する(私が使った)WM
    • tinywm: 50行程度の非常にプリミティブなウィンドウマネージャ、C版とPython版がある。
    • xpywm: 1000行程度のWM、Python
    • hogewm: xpywmをベースとしたWMで850行程度、Python

3.2. Xlibの仕様を読もう

  • Xのラッパーライブラリを操作してWMを作ることになる。その仕様書を読むこともまた、必須に近い。
  • もとはC言語用のライブラリで、仕様書は英語だが、幸い日本語の翻訳が存在する。
  • ウィンドウマネージャを作るときに重要となる項は、ウィンドウ情報関数、イベント、イベント処理関数などだと思う。
  • また、Arch Linux WikiとForum、Stack Overflowなどの技術系掲示板がたまに役に立つ。

3.3. 他のWMを使おう

  • 開発中もX上でコードを書いたり、それを実行したり、ブラウザで音楽を聴いたりすることもできる。
  • しかし、ウィンドウマネージャなしでそれらを制御するのは不可能ではないが非常に難しい。
  • そのため、開発初期は他人が作ったWMをを使うべきだ。
  • 上記のWMを使うのが手っ取り早い。

3.4. 一つのウィンドウで開発しよう

  • 開発初期のWMは、入力フォーカスを切り替えることすらままならない。
  • そのため、一つのウィンドウで全ての開発に必要な操作が完結するほうが良い。
  • 同じウィンドウ上でターミナルエミュレータが使えるテキストエディタを使うと良い。
  • 私はEmacsを使ったが、まともなエディタならたやすくできるだろう。

3.5. ログを出そう

  • 標準エラー出力(Xのエラーや例外も標準エラー出力に出てくるため)に常にログを出して動作を監視しよう。
  • 普段使いするときも、ファイルにログをリダイレクトしておくことで、バグを発見したときの再現が容易になることがある。(上記の.xinitrcでは捨てているが。)
  • (少なくとも私の)WMのそれぞれの処理はイベントの送出から開始される。そのため、少なくとも各イベントハンドラの呼び出し時にはログを出すのが望ましい。

エラトステネスの篩の高速化

 少し前に某会社の採用試験問題(?)の解説みたいな感じでエラトステネスの篩が話題になっていたので書こうと思う。C++を使うよ。

エラトステネスの篩とは

  • ある数以下の素数を比較的高速に求めるアルゴリズム。数の集合から素数の倍数を取り除くことで最後に素数が残る。
  • 以下はlimit以下の素数を格納したstd::vector<int>を返す関数の実装。
std::vector<int> simple_eratosthenes(const long long limit) {
    std::vector<bool> data(limit+1, false);
    data[0] = true;
    data[1] = true;
    for (long long s = 2; s*s <= data.size();) {
        for (long long i = s*s; i < data.size(); i+=s) {
            data[i] = true;
        }
        s++;
        while (data[s] == true) {
            s++;
        }
    }
    std::vector<int> result;
    for (long long i = 0; i < data.size(); i++) {
        if (data[i] == false) {
            result.push_back(i);
        }
    }
    return result;
}

やったこと

  • 最初から偶数を取り除いておくのと同様に、2, 3, 5, 7...など任意の数までの倍数を取り除けるようにした。
  • 2の場合だと、 2x+1のみが残る。
  • 3を含めると、 6x+1, 6x+5のみが残る。
  • 5を含めると、 30x+1, 30x+7, 30x+11, 30x+13, 30x+17, 30x+19, 30x+23, 30x+29 のみが残る。
  • これらのそれぞれを別々の実行スレッドに投げて並列化する。
  • 2の倍数を取り除くと0.5倍、3の倍数まで取り除くと0.33倍、5の倍数まで取り除くと0.267倍というようにメモリの消費量も減る。
  • それぞれに素数は同じ感じで分布しているらしいので並列化のムラはほとんどない。

実装関係の話

  • 上記のsimple_eratosthenesを使って探索範囲の平方根までの素数を求める。
    • simple_eratosthenesはメインの探索を行いながら、動的に篩うための素数を探しているが、マルチスレッド化するためにはこの方法だと別のスレッドからデータを読まなければならない。これはだるい。シングルで事前に計算した方がいい。
  • 各スレッドで、各素数を篩うときに開始位置を算出する必要がある。(例えば、 30x+13=41yを満たすxを求める。(答えは 943=41\times 23=30 \times 31+13\therefore x=31 ))
    • これは二元一次不定方程式なので、拡張互除法などで比較的高速に解ける。
    • 剰余が1となる場合だけ求めておけばあとはそれを何倍かすれば他はもとまるので、スレッドを分割する前に1についてのみを計算しておき、あとはスレッドに任せる。
    • 例えば、 30x_1+1=41y_1を満たす最小の自然数  x_1 は、 451 = 30\times15+1 = 41\times11\therefore x_1=15となる。
    •  30x_2+13=41y_2を満たす自然数  x_2 は、 x_2\equiv x_1\times13\bmod41である。 x_2\equiv195\bmod41なので  x_2=31となる。
  • シングルで求める比較的計算量が大きい部分はこの二つだが計算量は O(\sqrt{n \log n})程度なので、メモリの確保やメインの処理に比べれば雀の涙。(メモリの確保の方が圧倒的に時間がかかる。)

時間計測

  • 私のノートパソコン(MacBook Pro (13-inch, 2018, Four Thunderbolt 3 Ports))で行う。CPUはIntel(R) Core(TM) i5-8259U CPU @ 2.30GHzで、メモリは8GB 2133 MHz LPDDR3
  • コンパイラgcc version 10.2.0 (Homebrew GCC 10.2.0_4)を使い、最適化オプションを盛る。
  • 前述の篩うための素数を探す計算と、開始位置を探す計算は無視する。(1012までやっても14msしかかからない。)
  • メモリの確保にかかる時間は無視する。(今回やりたい評価には無関係なので)
  • 結果の素数をカウントし、その個数を以て正しさを評価する。
  • 素数の個数については、みんなの知識 ちょっと便利帳に掲載されているものを使う。

結果

  • 何個の素数を使って分割するかをbase_sieve_sizeで表す。
  • thread数、圧縮率以外の単位はミリ秒
  • すべて一発取りなので結果はあくまで目安
base_sieve_size 3 4 5 6 7
thread数 8 48 480 5760 92160
圧縮率 3.75 4.375 4.8125 5.21354 5.53939
107 1 1 12 309 44406
108 10 6 13 307 52329
109 684 109 57 325 60774
1010 15246 7033 907 696 62785
1011 208788 177030 68373 8994 58088
  • 結果は全て正しかった。

考察など

  • 問題サイズを大きくした場合、大量のスレッドに分割した方が速くなっている。
    • 問題を細分化したことにより、一つ一つのスレッドがキャシュに乗るようになり、動作が高速になったものと思われる。
    • base_sieve_size = 7の時は基本的に遅いが、問題サイズによる変化が最も小さいので、1012以上の大きさならばより高速に動作する可能性がある。
  • 1011base_sieve_size = 6で計算するために、すでに2.23GBのメモリ領域を使っている。メモリが足りないので1012までを一気に求めることはできない。
    • 計算結果を順次捨てながらメモリ領域を次のスレッドに引き継ぐようにすればメモリ不足は解消できるが、それは別の機会にやると思う。

ソースコード

github.com

巨大なjsonをいい感じに閲覧する

 クソゲーデータマイニングは、gameparams.jsonのデータを見る感じとなっています。しかし、gameparams.jsonはファイル容量にして226MB、615万行、2.26億文字のかなり巨大なファイルです。一般的なテキストエディタ、(atomsublime textなど)では開くことすらままならない上に、開いたとしても最表層だけで13000要素弱あるので、自分の求めるデータに辿り着くのは容易ではありません。

  そこで、それなりに高速でそれなりに機能的なCUIビュワーを作りました。(適当なプログラミング言語が扱える人は、自分でスクリプトを書いた方が楽だとは思います。)読み込みに数秒時間がかかりますが、各命令は基本、瞬間的に動作します。  

Thanks & References

  • TTaro_

 gameparams.jsonは以下の記事の内容を使って取得しています。さまざまな都合により、私のGithubにgameparams.jsonは存在しないので以下の記事を参考に自分で取ってきてください。

kawaii-14.hatenablog.com

C++によるjsonファイルの読み込みにはnlohmann jsonを使っています。非常に高機能で高速で書きやすいです。

github.com

 

Download

Installation

  • このプロジェクトのレポジトリにはjson_splitとjson_viewerが入っています。json_splitは関係ないのでjson_viewerに入ってください。
  • makefileがあるのでmakeしてください。
  • ビルドには、C++11以上に対応したコンパイラとnlohmann jsonが必要です。各々の環境に合わせてインストールしてください。

使い方

開き方、閉じ方

  • ./jsonviewer.out fiilename.jsonのように開きます。
  • (gameparams.jsonの場合)数秒の待ち時間の後、>のマークが表示されます。この状態の時に命令を入力することでいろいろできます。
  • ctrl+c, ctrl+d, 空の命令(改行のみ)を入力すると終了できます。

命令一覧

  • list option

    • optionと部分一致した子要素を表示する。
    • 何も指定しない場合は全部表示する。
    • 標準出力
  • find condition | condition & condition...

    • 条件指定リスト
    • 条件が指定できる以外の動作はlistと同じ
      条件
      • 条件はディレクトリ/ディレクトリ/要素名 = 値のように記述する。
      • 現在のディレクトリの子要素に、ディレクトリ/ディレクトリ/子要素があるかどうか検証され、ある場合は値と一致すればlistのように表示される。
      • 対応しているのは数値(整数、実数)、真偽値(true, false)、文字列(""で囲まれた範囲、エスケープシーケンスには未対応)、null
      • 配列はディレクトリや要素側で、要素[0]のように指定できる。
      • 数値は大小関係(以上以下、より大きいと未満)が比較できる。
      • 要素とリテラルの比較は実装しているが、要素同士の比較は実装していない。
      条件の接続
      • | 論理和
      • & 論理積
      • ^ 排他的論理和
      • 条件は常に左優先で評価される。
      • a|b&c^dと書くと((a|b)&c)^dと評価される。
      • 括弧で囲んで順序を規制したりするような機能は実装していない。すまねぇ
  • select option

    • optionは番号
    • 直近のlist、findで表示した番号の子要素に移動する。
    • move, backを実行するたびに番号は消える。
    • エラーなどは標準エラー出力
  • move option

  • back

    • 親の階層に移動する。
    • 普段は何も表示しない。
    • エラーなどは標準エラー出力
  • dump option

    • optionと完全一致した要素以下を展開して表示する。
    • 4文字のソフトタブにのみ対応している。
    • 何も指定しないと、直近のlist, findで表示した要素を全て展開して表示する。
    • 標準出力(エラー以外)
  • output filename command

    • filenameというファイルを作成し、commandの内容(標準エラー出力以外)をそのファイルに書き出す。
    • listとdumpにしか効果はないが、commandにそれ以外のコマンドを指定しても動く。

実行例

1.全ての要素のリストを得る

  • listコマンドで得られます。

2.Ohioのデータに移動する

  • list Ohioで"Ohio"という文字列に部分一致する最表層の要素を一覧します。
  • リストの0番がその要素なので、select 0で選べます。

3.Ohioの全長を調べる

  • 全長は、A_Hull/sizeの配列の0番目の要素に格納されています。
  • 先程の状態から、move A_Hulldump sizeで表示されます。
  • 全長は280.77mらしいです。

4.rootに戻る

  • backコマンドをを2回実行すると戻れます。
  • moveコマンドをオプションなしで実行するとrootに戻れます。

5.全ての空母のリストを得る

  • 空母はtypeinfoのspeciesが"AirCarrier"になっています。
  • そのため、find typeinfo/species = "AirCarrier"で表示できます。

6.Σ値が1.7である艦艇のリストを得る

  • sigma値はA_Artillery/sigmaCountか、A1_Artillery/sigmaCountに書いてあります。
  • find A_Artillery/sigmaCount = 1.7 | A1_Artillery/sigmaCount = 1.7で表示できます。

7.全長が250mより長い巡洋艦の全要素を、longCruiser.jsonに保存する

  • 全長はA_Hull/sizeの0番目の要素に格納されており、巡洋艦はtypeinfo/speciesが"Cruiser"になっています。
  • よって、これを満たす要素のリストは、find A_Hull/size[0] > 250 & typeinfo/species = "Cruiser"で得ることができます。
  • その状態でdump命令を実行すると、表示されている全要素を出力するので、その中身をoutputコマンドでlongCruier.jsonに書き込みます。
  • コマンドは、output longCruiser.json dumpとなります。

終わりに

  • 命令は全て標準入力から読んでいるので、適当なテキストファイルにクエリを書いておいて、./jsonviewer hoge.json < query.txtのような感じで自動化できます。
  • 命令のパーサー(特に文字列周り)がゲボカスなのはわかってます。気が向いた時に直すかもしれませんが、クソゲーのマイニングに使う分には問題ないので直さないかもしれません。
  • バグ等あれば、Twitterかissueで優しく(再現できる条件を)教えてください。

update note 2021/4/19

  • 配列は値ではなく無名要素で構成されたオブジェクトとして扱うようにした。