欲しかったターミナル(端末エミュレーター)

昔から頭の痛い問題だった事を一つ片付けた(かもしれない)話です。

いきさつ

私は、多数のPCで並行作業をやることが多く、それぞれの環境で完全に開発環境を一致させるのも 保守的にダルいと言うこともあって、環境のデフォルト構成を重視する傾向があります。

例えば、現在は主にUbuntuを使っているのですが、そこに用意するアプリケーション、 例えばvscodeやらjetbrainsなどのIDE類、小さなところで言えばbashの構成など、 できるだけデフォルトのままで使うようにして、 別の環境でもこれらのデフォルトが同じかあるいは近しいものを選択して、できるだけ自分で構成を弄らない、 みたいなことに拘ってしまいます。

これの背景を語りだすと、つまらない話がとめどなく流れてしまうのでざっくりと説明すると、 昔 NFS+NIS が全盛だった頃に、アカウント情報とホームディレクトリの完全一致の守護者、 みたいなことに心血を注いでいた黒歴史があり(これは後にADでもやって結構な地獄を見た)、 その反省から来ています。

なので、異なる環境でも同じように使用できて、この環境を実現するのに苦労しない、 みたいなことがあると大変うれしいのです(スイッチングコストが辛いというのも大きい)。

そして、それは「端末エミュレーター」のジャンルにも言えます。

gnome-terminal

これはUbuntu標準のgnome-terminalなんですが、使っていると不満が出てくるんですよ:

  • フォントサイズの変更がダルすぎる。そしてキーバインドでvscodeなどと同じように Ctrl+Plus や Minus で変えられるようにしたい(これは今のバージョンでは出来るようになっています)。
  • 多数のターミナルを開くと、どれがどれかわからなくなる。
  • リモート接続をsshでやるのがダルいので Remmina を使うけど、そうすると gnome-terminal じゃないのが不満。
  • 同様に、IoT開発でシリアルデバイスやるのに minicom とか思い出すのが大変。
  • 更に、vscode上でターミナルを開いていると、更に「ムキーッ!」って叫びたくなる。同じように、同じようにやりたい... 微妙に操作やキーバインドが違うのは辛い...
  • ウインドウサイズ変更したい時にマウスでボーダーを掴みにくい(これはウインドウマネージャやコンポジタの問題ではあるけど)
  • 上記に絡み、そもそもボーダーが視認しにくい(特にDark theme)。

他にも色々あった気がするけど忘れました。 それで、これだけモヤモヤしていると、ふと気がつくわけです、普通なら考えないことを。

「そこまでフラストレーションが溜まるぐらいなら、天秤にかけて、端末エミュレーター作ったほうが良くないか?」

幸いなことに (?) 最近であればCodexが手を貸してくれそうです。

端末エミュレータの核

端末エミュレーターは、一昔前なら不満があるからと言って自分で実装しようなどとは思わないジャンルです:

  • 受信したデータ列をストリーミングパースして、エスケープシーケンスや特殊コードに対応した処理を実現させる
  • ASCII範囲ならいざ知らず、UTF-8の解釈、多国語領域のコードのレンダリング(CJKやその他)、合字、RTL、絵文字などなど
  • キー入力、IME制御とインライン編集バッファ処理
  • 最近だと、Kitty protocolなどのリンクサポートやよりリッチなインタラクションのサポート

いや、ウインドウ表示してテキスト表示して、できたできたーってやつなら簡単なんですが、目的が実用になった途端に難易度がプラチナ級です。

で、現代では端末エミューレーターを構成するのに補助ライブラリもいくつかあって、例えば libvtermlibtsm、JavaScriptで使える xterm.js などがあります。 これらを使用すると、上記の実装部分のコアになる部分は手が抜けますが、UI周りの実装はまだまだ大変です。

で、いくつか見た感じで良さそうに見えたのが、 libvte です。 これは、GTK上に構築された端末エミュレーターのウィジェットライブラリで、要するにこれの外側に肉付けしてやれば、端末エミューレーターアプリが作れてしまうというスグレモノだと言うことが分かりました。

実際、gnome-terminalはこのlibvteを土台としています。

特に、今回は端末エミュレーター自体を深く追いかけたいわけではなく、できるだけ可及的速やかに満足出来る端末エミュレーターを作って実用する、ことが目的なので、落とし所としては十分でしょうきっと。

出来上がった

それでは見て下さい:

名前がアレだって? ようつべで散々TES4/5のBGM流しながら作ったので、少し侵食されたことは認めますが、名前のとおりです。 「あの頃」が現代の技術で再現されている、と聞いて興味を持ったなら、あなたにもフィットするかも知れません。

誤解を恐れずに言うなら、大御所のTeraTermさんと更にその昔の数多のシリアルターミナルアプリケーションが魔改造され、モダン化でドーピングした、そういうものです。

仕様だけです。中身はフルスクラッチです、念の為。

Local terminal

これがターミナルウインドウです:

えっ、なんかフツーのターミナルで何が良いのか良くわからないって? そうでしょうそうでしょう、まず「奇抜性」は狙ってません。 普段使いするのです。gnome-terminalの代わりに使うのだから、背景が透過するだとか、デコやアニメーションが凝ってるだとかは不要なのです。 でも、それだとgnome-terminalを使うのと変わらないのでは、という感じもしますが、もちろん見えないところで色々違います。

フィーチャー

  • Linux GTK/libvte を使用して、飾らない、必要十分なターミナルを実現します。
  • シリアルやTELNET接続だけではなく、ローカルターミナル、SSH、SFTPもサポートし、現代で同じ生活を取り戻せます。
  • ランチャーから複数の接続先を管理したり、そこからターミナルの起動を行えます。
  • 接続先の設定は、全てINI形式のファイルに保存されます。 INI形式なので、自由にエディタで再編集出来ます。
  • BS/DEL、カーソルキー変換、Enter/Returnコード、文字コード変換を指定できます。
  • ターミナルの行数と桁数を、接続毎の設定として記憶出来ます。
  • TELNET/SSHで、ターミナル種別 (xtermxterm-256colorなど) を明示的に指定できます。
  • X/Y/ZMODEMファイル送受信をサポートしています(ローカルターミナルを除く)。 ZMODEMなら、自動送受信を有効化出来ます。
  • SFTPで、SSH接続を行うホストとファイルの送受信を実行出来ます。
  • テキストのペーストやテキストファイルの送信を実行出来ます。 その際に、送信速度と改行コードの扱いを指定して、ホスト側のバッファオーバーフローや改行コードの不一致を回避出来ます。
  • インジケータがターミナルの下部に表示されます。アナログモデムを思い出して、余韻に浸れます。
  • BELに、組み込みbeepまたは任意のWAV/Ogg Vorbisサウンドを指定出来ます。
  • フォントサイズをショートカットキーで変更したり、マウスのホイールで変更したり出来ます。
  • 接続毎にウインドウのエクステリアカラーやターミナルの背景色をカスタマイズ出来ます。
  • ホットキー一発で、指定された接続を起動出来ます(Waylandでは、XDG Global Shortcutsポータルが必要)。 まずは、ローカルターミナルをホットキーで起動できるようにすることから始めて下さい。 全てがelder-termsに置き換わるのも、時間の問題かもしれません。
  • ランチャーを自動起動させたり、システムトレイに常駐させることが出来ます。
  • 受信文字列を正規表現で監視して、自動的に文字列を送信したり、指定されたコマンドを実行できるルールを指定出来ます。
  • OSC 8ハイパーリンクをCtrlキーを押しながら左クリックして、正規表現から抽出したパスや行番号を任意のコマンドへ渡せます。
  • ログをファイルに記録できます。接続先や日時によるディレクトリの分離によって、ログの整理が捗ります。
  • 多国語表示に対応しています (英語、アラビア語、スペイン語、フランス語、ヒンディー語、日本語、韓国語、ポルトガル語、ロシア語、簡体字中国語)

いくつかスクリーンショットを貼っておきます:

ランチャー

Launcher

ターミナル設定

Terminal settings

複雑な表示もOK

Complex terminal

色指定

Colored terminal

シリアル接続

Serial terminal

SFTP

SFTP

内部の話

フィーチャー一覧で気になった人は、日本に2〜3人ぐらいは居るかも知れません。elder-termsはX/Y/ZMODEM転送をサポートしています。

X/Y/ZMODEMのプロトコルスタックとして、著名な lrzsz を使おうとしたのですが、いくつか問題がありました:

  • アプリケーション組み込みには構造上向かない。lrzszはCLIプログラムとして作られていて、ライブラリとして使用するには微妙だった
  • ライセンスが不明瞭。elder-termsはMITで公開しようとしていたため、そのまま持ってくるわけには行かなかった

Codexを使うのだから、X/Y/ZMODEMの仕様書があれば一から書かせることも不可能ではなかったハズなのですが、 形式的な仕様書がなく、昔書かれた網羅的ではないドキュメントやソースコード、ネットニューズ(NNTP)の言い伝え(?)みたいなやつしか参照できません。

そこからブレークダウンして仕様を確定、あるいはテストベンチの検討といった泥臭い作業は自動化出来ないため、壁打ちを繰り返して確定させていきました。

一応、すごく昔にZMODEMをTurbo Pascalに移植したことがあるので、 全く心得がなかったわけではありません。 そのコードも一応某所に未だに公開されているので、 今回自分の書いたコードをダウンロードして、 本当に久しぶりに眺めて、そして黒歴史大ダメージを浴びてしまう実績を解除しました。

特に面白かったのが、Codexが散々X/Y/ZMODEMの基本的な環境前提を誤解し続けたことです。

X/Y/ZMODEMは、現在接続中の通信経路を「横取り」して、そこにそのままバイナリプロトコルを乗せます。 どうもこれが理解できないようで、プロトコルの開始や終了でptyとの接続切り替えを行っていなかったり、libvteとX/Y/ZMODEMのパーサーの両方にデータを同時に流したり、手順終了時にパーサー切断と同時にptyも切断してしまったりと。

「いや、小難しいことを考えずにもっとリラックスして単純に考えれば良いんだよ」と言いたくなるようなミスを連発していました。 まあ、こういうプロトコルの例は現代にはあまりないのと、そもそも資料の学習が希薄であろうことは想像できるので、ここもやはり学習の物量がものを言う世界なんだなと、改めて感じたことでした。

そういうわけで、X/Y/ZMODEMのプロトコル処理だけを純粋に取り出した libxyzm を作りました。もちろんMITです。 このライブラリは、同期バージョンと非同期バージョンがあり、同期バージョンは完全なるC APIなので、組み込み向けにも使用できるはずです(そのうち別のなにかに使おうとは思っている)。

また、非同期バージョンでは C++20 の co_await を使用して、同期バージョンと同じような書き心地でコードが書けるようにしてあります。

しれっと爆弾発言したなと思った方が更に少人数いたかも知れませんが、C++20の co_await を使用可能にしているのは cardio で、libxyzmはこれを使用しています。

cardioの話をし始めるとまた長くなってしまうので省きますが、elder-termsの全ての処理はcardioを使用していて、当然libxyzmも非同期バージョンを使用して、全面的に非同期処理で実現していて、ワーカースレッドを立てないようにしています。この辺りのこだわりは .NET から来ています。

elder-termsの開発は、だいたい2ヶ月ぐらい掛かっているのですが、libxyzmとcardioを開発・整備する時間も含まれているので、これらのお膳立てがあれば半月ぐらいで完成したんじゃないかと思います。 なお、elder-termsは完成間近の段階で一回完全に捨てて作り直しています:

  • ほとんどのフィーチャーは実装済みですぐ使える状態だったけど、保守に不安を感じる品質
  • 捨てる決断は結構辛かった。折れかかった...
  • この過程で、非同期処理の包括的な土台の必要性を痛感した結果、cardioが生まれた

使用感

今回(いつもだけど)、実用できる事を目標にしていたので、自分で作っておきながらその使用感にわくわくしていました。結果として:

  • ホットキーで接続エントリに対応するターミナルを即開ける。
  • 接続エントリ毎に異なる外郭色を指定できるので、ひと目で何のターミナルかわかる。
  • ボーダーがつかみやすい、視認しやすい!
  • 実用レベルで普通に軽い(GTK使っているので極端にリソースが足りない環境では苦しいと思います。その点は未評価)
  • gnome-terminalをUbuntuのランチャーから外すことが出来た。
  • debでインストールするだけなので、ギリ管理可能。
  • TELNETしか使えないシチュエーションでも同じように使える。シリアルも同様。
  • インジケーターがとても良い。可視化重要。
  • 代替フォント指定が正しく機能する。
  • オートパイロットが可能なマクロと全ログ記録。

一方、まだ課題があると感じた部分:

  • libvteにちょくちょく問題がある:
    • GTK4が未だにIME絡みの問題を抱えていて、GTK4に移行できない。
    • フォント周り(特にサイズ変更)に非常に泥臭いハックコードを書かざるを得ず、回避できない。
  • 接続先エントリ毎の外郭色だけではちょっと分離として不足感がある:
    • 例えば作業中ディレクトリ毎に異なるターミナルを何とか視覚的に一瞬で区別出来るようにしたい。
    • これはLLM開発系でそのような要望に答えるターミナルがいくつか開発されているらしいので、参考になるかも知れない。
  • SFTPはもちろん良いけど、FTPも必要だった。なんで考えなかったのか。

ウインドウアレンジをウインドウマネージャやコンポジタに任せる、という判断を早い時期にしたので、全体的な設計としてウインドウ配置(タブ化も含む)の制御を放棄していて、そこにあまり不満は感じてはいません。 しかし、Ubuntuのその辺りがあまり自分とマッチしていない、というのは漫然と感じています。

特にタブ化はやらないリストに加えたぐらいで、それ自体は問題ないのですが、 ディスプレイという限られたスペースに、もう少し論理的な枠組みでターミナル群を配置できるような、良いアイデアが無いかなぁ、と感じています。

ええ、私は Windows 1.0 を割と良い妥協点だと思ってますよ。 その当時は全く魅力的に見えませんでしたが...

それではまた。

C/C++でWASMをサクッとやりたい

少し前にWASMの開発をやることがあって(maplibre-gl-layersの話を参照)、そこでWASMのコードを書きました。 WASMと言っても、今だとRustを使うことが多いのかもしれませんが、私はまだRustをやっていない(Linux kernelが置き換わるまでには...)のと、 C/C++には慣れているので、C/C++でWASMコードを書きたいと思いました。

実際のところ、開発時間の問題であまり悠長にコードを書いているわけにも行かなかったので、今回は(も)諦めたという

それで、C/C++でWASMのコードを書く場合は、 Emscripten SDK を使うのが定番ということで、 そのまま採用することにしたのです。 ただ、C/C++で複雑なコードを書くわけではなく、今回は殆ど座標計算を大量に高速にやらせたいというのが目標だったので、 ぶっちゃけ、C/C++のエコシステム、具体的には外部ライブラリなどを使う可能性は殆どありませんでした。 せいぜい、std 名前空間のコレクションなどを使うぐらいでしょう。

そういうわけで、WASM開発でも恐らく定番となる CMake は使わず、殆ど直に生書きという選択肢を取りました。 そこまでシンプルなら、Makefileでも良いんじゃないのと言うつもりだったのです。

環境セットアップの問題

ただ、環境整備が面倒なんですよね。と言っても、Emscripten SDKを使う場合は、リポジトリをcloneしてきてセットアップスクリプトを実行すれば、 ホスト環境で必要なtoolchainをダウンロードしてきてくれるので、何が面倒なんだよ configure もしないじゃん、というツッコミはあるかもしれません:

git clone https://github.com/emscripten-core/emsdk
./emsdk install latest
./emsdk activate latest

仕事で使うので、環境再現性が高くないと色々辛く(例えばCI)、あまり手動セットアップで便利でもそれはそれで困ってしまいます。 もしこれをCIでやる場合は、Emscripten SDKをどこにcloneすれば良いのか(あるいはしなければならないのか)、 スクリプトがどのように環境を整備するのか、ということを把握していないと問題が起きそうです。

一度だけで済めば良いが...

そういうわけで、これをできるだけ簡潔な環境で簡潔な手順で再現性のある方法で実現しておかないと、色々なことをやっている自分には覚えておける記憶スペースがなく、 後で「これどうするんだっけ」みたいに激しく後悔することになります。

Makefileはまあシンプルですが、Emscriptenの全体的になんとかしてシンプルにしたい。楽にやりたい。 そこで、これらの問題をひっくるめて簡潔に完結させる何かが必要だと感じで、それなら作るかそこまで大変でもなさそうだし(フラグ)、と考えたやつです。

emsdk-env

で、作りました。 "emsdk-env" です。

TypeScriptの開発を始めてからネックだったことの一つは、ビルドプロセスのライフサイクルの標準的な環境が存在しないことです。 NPMのscript定義があるだろう、と言われそうで、やれなくもないですがその辺り弱すぎるので、今はViteをその目的に使っています。

Viteを前提にすれば、プラグインシステムでかなり柔軟にビルドプロセスに介入できます。 今回のような目的にぴったりです。

ただ、世間一般では、ViteはVueやReactのフロントエンド開発で使われているようで、本当は少し毛色が違うとは感じています。 特に自分はフロントエンド開発は片手間のような感じなので余計に。 しかし、他に良い選択肢も無いので、ちょっと無理がありますがすべてのTS開発でViteを採用しています。

つまり、Node.jsを使うようなプロジェクトでも、Viteを使ってビルドしています。 まだ色々課題はありますが、ElectronでもViteを使おうとしています。 これと全く同じ流れで、既に様々なViteプラグインを作っていますが、それらの解説はまだ今度。

話をemsdk-envに戻すと、これは、誤解を恐れずに言えば、Viteプラグイン化した"make"です:

  • Emscripten SDKの自動セットアップ・キャッシュ
  • Viteプラグインによる、HMR対応(但しC/C++コードは全体ビルドが行われます)
  • 並行ビルド対応
  • エクスポートシンボルの簡易指定が可能
  • 複数のターゲットWASMバイナリを生成可能
  • ディレクトリパス・コンパイルオプション・リンカオプションのカスタマイズが可能
  • アーカイブライブラリ(*.a)のビルドと参照が可能
  • NPMパッケージでWASMライブラリの配布と参照が可能

どうやってWASMコードを実行できるか

こんなリストよりも、ビルド定義をどう書くかの方が、一目瞭然でしょう。 まず、インストールして:

npm install -D emsdk-env

vite.config.ts にプラグイン構成を加えます:

// `vite.config.ts`
import { defineConfig } from 'vite';
 
// emsdk-envのViteプラグインを参照
import emsdkEnv from 'emsdk-env/vite';
 
export default defineConfig({
  plugins: [
    // プラグインとして追加
    emsdkEnv({
      // WASMローダーコードを生成
      generatedLoader: { enable: true },
      // ビルドターゲット
      targets: {
        // "add.wasm"を生成
        add: {
          // コンパイルオプション
          options: ['-O3', '-std=c99'],
          // リンクオプション
          linkOptions: ['--no-entry'],
          // リンクディレクティブ
          linkDirectives: { STANDALONE_WASM: 1 },
          // エクスポートシンボル
          exports: ['_add'],
        },
      },
    }),
  ],
});

targets の辺りが、だいたい Makefileのように機能しますが、とにかく楽に最小限の定義で使えるように工夫してあります。 とは言ってもWASMでEmscripten SDKを使う場合のお約束、 STANDALONE_WASM--no-entry のような最低限のオプション指定は必要です。

それでも、そこさえ押さえれば、あとはJS世界からWASMで定義した関数が見えるように、 exports にシンボル名を書くだけで行けるようになっています。 以下のようなプロジェクトディレクトリ配置を行って:

project/
├── package.json
├── vite.config.ts
├── src/
│   ├── generated/
│   │   └── wasm-loader.ts   // (自動生成)
│   └── wasm/
│       └── add.wasm         // (ビルドされたWASMバイナリ)
└── wasm/
    └── add.c

Cのコードを書くだけです(もちろんC++でもOK):

int add(int a, int b) {
  return a + b;
}

WASM開発を行う場合は、もう一つ、どうやってTSからWASMに定義した関数を呼び出すのか、という問題があります。 これも毎回面倒なスタブ実装を書く必要があるのですが、emsdk-envでヘルパーコードを生成できるようにしてあります(generatedLoader)。 これが有効なら wasm-loader.ts ファイルが生成されるので、あとはこれを使うだけです:

import { loadAddWasm } from './generated/wasm-loader';
 
// WASMエクスポート関数の定義 (手動で定義が必要です)
interface AddExports {
  add?: (a: number, b: number) => number;
}
 
// WASMバイナリをロードして使用可能にする
const wasm = await loadAddWasm<AddExports>();
 
// `add()`関数を取得
const add = wasm.exports.add!;
 
// 関数を実行
const result = add(1, 2);

他にも様々なオプションや、ビルド戦略に使用できる柔軟な定義が出来るので、興味があれば READMEを参照してください (詳しく記載しています)。

重要な機能

重要な機能の説明を忘れるところだった... emsdk-envはViteプラグインなので、HMRに対応しているんですよ (!!!)

つまり、npm run dev などでVite devサーバーを起動しておき、C/C++のソースコードを編集して保存すると、ブラウザプレビューに反映されますよ。 もちろん、TS/JSのように、ページの部分更新まで含んだ真のHMRというわけには行かず、C/C++コードはフルビルドが発生します。 それでも、これは非常に開発体験が良いです。

一応、平行ビルドを行うようにして、あまりTS/JS側のHMRと見劣りしないように頑張ってはいますが、強いて挙げるならここは今後の課題です。

それともう一つ、emsdk-env向けのディレクトリ構成を守ってNPMパッケージを生成すれば、WASMパッケージを作れます (!!!!!!)

つまり、あなたのWASMライブラリをNPMで簡単に取り込んで使えるようになるのです。 例えば、作ったライブラリのヘッダファイルとアーカイブファイルがあれば、それらを取り込んだNPMパッケージを作り、 使用者はNPMパッケージをインストールして:

export default defineConfig({
  plugins: [
    emsdkEnv({
      // "wasm-calc-lib"パッケージのライブラリを使用する
      imports: ['wasm-calc-lib'],
      targets: {
        // "offload.wasm"
        offload: {
          // "wasm-calc-lib"パッケージの"libcalc.a"を参照
          linkOptions: ['-lcalc'],
 
          //  :
          //  :
        },
      },
    }),
  ],
});

のようにするだけで、そのライブラリを使えるようになります。 これは、私がVS2013の頃に、ライブラリのパッケージングで考えていた事(WASMの話ではありませんよ! その頃にはまだWASMは無いです)で、 今になって(環境は違うけど)やっと具現化したよなぁと言う、ちょっと胸熱な機能です。

その他

ヘルパーコードは、型定義までは踏み込んでいないため、エクスポート関数の定義は手で書く必要があります。 ここはTypescript APIを使用すれば自動化できることは分かっているのですが、ビルド時間に影響があるので現在はそこまでやっていません。 なお、 別の方面から攻めること も考えています。

TS APIは6.0でも(高速化について)消極的のように感じたので、サポートするかどうかはまだ未定です。

まとめ

emsdk-envはViteプラグインで、Emscripten SDKの自動セットアップやビルド管理、HMR対応を行います。 CMake を使うような大規模開発ではなく、もっと小規模で高速開発を行いたいのなら、良い選択肢だと思います。

maplibre-gl-layers や、 massive-sprites で、実際に使用しているので、参考にしてください。

第11回Center CLR勉強会

Center CLR No.11

忙しかったので準備も大変だったけど、終わってからもこれを書く時間が取れずにもう木曜日...

今回の私の登壇は、去年(2025)の成果の2つを (忘れないうちに) 発表するという感じでした。

伝えるべき情報を結構絞ったのですが、それでも1セッション辺り60ページ超えてるので、 1ページ1分弱のペースで喋る必要があり、ずっと喋り続けて喉が痛い...

でもこれで安心して脳の記憶スペースを空けることが出来たので、肩の荷が降りました (忘れないように色々覚え続けておくのは辛い)。

後で動画公開しようとして録画はしたのですが、残念ながら音声不良のためボツとなりました。

以下に私のセッションの内容について少し紹介します:

あなたが必要だったNuGetサーバー

nuget-server - あなたが必要だったNuGetサーバー
by Kouji Matsui
speakerdeck.com
Kouji Matsui

キャッチーなタイトルを付けてしまった...

一言で言うと、2025年現在 (もう去年だけど) 、プライベートでサクッと立ち上げることが出来る定番のNuGetサーバーが存在しないと思われるため、 自分で作った、という話です。

フィーチャー:

  • NuGet V3 API互換性:最新のNuGetクライアント操作をサポート
  • データベース管理不要:パッケージファイルとnuspecをファイルシステムに直接保存、データベース管理から解放
  • パッケージ公開:cURLやその他のツールでHTTP POSTを使用して.nupkgファイルを柔軟にアップロード
  • 基本認証:必要に応じて公開と一般アクセス用の認証を設定
  • リバースプロキシサポート:適切なURL解決のための信頼できるリバースプロキシ処理を設定可能
  • 拡張機能を備えたモダンなWeb UI:
    • 複数パッケージアップロード:複数の.nupkgファイルを一度にドラッグ&ドロップ
    • ユーザーアカウント管理:ユーザーの追加/削除、パスワードリセット(管理者のみ)
    • APIパスワード再生成:セルフサービスでAPIパスワードを更新
    • パスワード変更:ユーザーは自分のパスワードを変更可能
  • パッケージインポーター:既存のNuGetサーバーからのパッケージインポーター付属
  • Dockerイメージ利用可能

特に、ゼロコンフィグレーションにはこだわりました。要するに、これだけで起動します (要Node.js):

$ npx nuget-server

何でそうしたのか? というバックグラウンドはスライドで軽く触れていますが、セッション中も色々背景を話しました。 そして、実際に仕事でも使ってドッグフーディングしていて、公開直前まで調整を行っていました。

他にも、Dockerイメージについてはマルチアーキテクチャ対応をやってみたかったので、その対応方法について解説しています。 普段はpodmanを使っているので、podmanでのマルチアーキテクチャ、具体的にはamd64(x86_64)とarm64(aarch64)の両方に対応させています。 今だと、Raspberry Piの32ビット向けにarmv7に対応させる事も出来るかもしれません。

podmanを使う場合は、クロスアーキテクチャ実行が可能なので、普通にDockerイメージを起動するかのように、異なるアーキテクチャのイメージを実行できます (素晴らしい) 。 この機能を使って、amd64環境のUbuntu 24.04上でarm64のビルドも行っています。 但し、この機能はqemuを使って実現しているようです。なので、クロスアーキテクチャイメージを起動すると、それなりに遅いです。

解説中にも補足しましたが、アーキテクチャオプションの指定が --platform linux/arm64 なんですよね。 もしかしたらpodmanのチームは、(マボロシの) Windowsコンテナに対応させる気があるのかもしれませんね。

地図に移動体たくさん表示したい

maplibre-gl-layers - 地図に移動体たくさん表示したい
by Kouji Matsui
speakerdeck.com
Kouji Matsui

え、何で (私が) いきなり地図? という疑問があるかもしれません。私もそう思います。 (仕事で) 必要だから作ったという、いつもの流れです。

地図周りの仕事は、時々やるので、これが全く初めてというわけではないです。 そういえば高度(?)な言語習得に興味を持ったのも、3D市街地表示を表示する何かをTurbo Pascalで開発していたのをバイト先で見たから、というのもある... どうでもいいか

MapLibre GL はもう十分有名な地図表示のためのライブラリプロジェクトですが、 多数の移動体(移動する物体)を表示するには難しい問題があります (多数の動かない地物を表示するのは全く問題ない)。 それで、MapLibreには、いわゆるプラグインインターフェイスが存在するので、これを使って多数の移動体を表示できるように拡張できる 「レイヤーライブラリ」を実装した、という話です。

フィーチャー:

  • 大量のスプライトを配置・変更・削除できる。
  • 各スプライトの座標点を自由に移動出来る。つまり、移動体を簡単に表現できる。
  • 各スプライトには、座標のアンカー位置を指定できる。精密な位置の描画が可能。
  • 各スプライトには複数の画像を追加出来て、回転・オフセット・スケール・不透明度などを指定できる。同様にテキストも配置できる。
  • スプライトの座標移動・回転・オフセットをアニメーション補間出来る。
  • 画像の重なりを制御するための、サブレイヤーとオフセット指定も可能。
  • 完全命令型API。高性能かつ拡張性のある更新を実現。
  • WASMとシェーダーによる計算処理の高速化。

この取り組みでも、経験の無かった方面 "WebGL", "WASM", "SIMD" について取り組んでいます (SIMDはSSE2までは少しかじったことがある)。

WebGLやWASM未経験から始めて見えた部分や失敗したことなどを色々紹介しています。

私はネイティブC/C++開発については経験があるので、そういう低レイヤーから眺めるこれらの技術、というような視点も少しあります。 特にウェブ界隈は高レイヤーなアーキテクチャの話が多いので、なんだか「ブラウザのはらわたを弄ってる」感じがして面白かったです。

機能面で言えば当初目標は十分に達成しているので、このプロジェクトも良い感じで終えることが出来ました。

ところでこのセッションでは、WASMの開発環境としてC/C++ (Emscripten SDK) を使った、という話しかしなかったのですが、 Emscripten SDKの準備をするのが割と面倒 (難しくはない) という問題があって、これを簡単に実現する emsdk-env も作っています。

(だいたい、開発環境周りを整備するために必要な道具も作る、みたいな事をやってるから苦労するんだというのは自覚がある...) これについては、近々ここに説明を載せようと思います。と言っても、私の書いているプロジェクトは、必要な情報をREADMEに盛り込むことが多いので、 被らない範囲の何かを書くことになると思います。

次回の開催

まだ第12回は未定ですが、しゃべるネタは色々あるので、梅雨か夏の前ぐらいを目処にconnpassに立てると思います。 特に中部圏の方は、ぜひご参加下さい。

リブート

とうとうブログをリセットすることにしました。

経緯

というよりも、以前から(かれこれ10年弱?)賞味期限切れ状態で久しかったのでリセットしたかったのですが、 リセット準備と言う名のやるやる詐欺が横行した結果、今の今まで伸び伸びになってしまってました。

理由の一つは、WordPressから脱却して、自分で設計したドキュメントサイトジェネレータに移行することだったんですが、 最初のバージョンが途中で頓挫し、それから作り直して形にするまでに時間が取れなくて数年経ってたという...

その、昔の作りかけのコードをようやくR.I.Pとしてアーカイブすることが出来ました。完成させられなくてごめんな。おやすみ。

新しいジェネレータのコードはもう公開してあるのですが、これについては今まさにドッグフーディングを始めたところなので、 紹介はまた今度の機会にしようと思います。

自らを試すため、これを公開したと同時に、以前のレンサバをバックアップ取らずに即解約したぞ

過去の記事をコンバートする事も考えたのですが、WPの記事の機械的な変換が結構面倒なのと、 最初に示したようにもう賞味期限切れの記事がほとんどだったので、思い切って捨てることにしました。

あれを読んで参考になったと言ってくれた人も居たので、ちょい惜しい気もしましたが、どうか忘れてください :)

近況

そんなわけで、最初の記事として何を書こうかと考えたんですが、近日にCenter CLRでしゃべる事もあるし(しかも3枠)、 その後のネタもどう発表するか考えあぐねていたこともあって、いざ書けるようになっても何書いて良いのかあんまり思いつかない...

なので、今取り組んでいることをリストアップしてみようかと思います。

最近は、仕事と趣味両方ともTypeScript環境主軸で、一部C/C++でコードを書いてます。 仕事専用のものは公開できないのですが、一部のコードはOSSにしつつ、仕事でも使っています:

  • nuget-server: NuGetサーバー。プライベートまたはチーム・個人運用の公開向けNuGetサーバー。簡単に立ち上げることが出来て、認証周りも出来て、データベース不要でモダンな外観でDocker imageもあってコマンドライン一発で起動もできるやつ(今度登壇して解説)。
  • maplibre-gl-layers: MapLibre GL JS用のプラグイン的なライブラリで、多数(>1000)のスプライトを同時にアニメーション動作させることができる。車両や地物のアイコン表示、識別のためのラベル表記、自動追尾など、これ一つで「動くターゲット」を地図に多数表示できます(今度登壇して解説)。
  • funcity: テキストプロセッサで関数型言語を使いたかったので実装した、テキストプロセッシング言語処理系。完全に機能します(恐らく)。awk+sed とかの代わりに使うことを強く想定しています。そのとおりに使えるかどうかはしらんけど。
  • mark-deco: markdownをHTMLに変換する際に欲しくなる、細々とした付随機能を全部盛り込んだ、変換ライブラリ(このサイトジェネレータでも使用しています)。
  • async-primitives: TypeScriptで非同期処理(Promise)やるときに道具が無さすぎて困るので、まとまったライブラリを作った。
  • prettier-max: ソースコード整形のPrettierを自動的に走らせる、Viteプラグイン。deprecated検出とかもできる。ESLintの管理に疲れたので作った。
  • screw-up: TypeScriptなどのトランスパイル結果の成果物に、バージョン番号等を自動挿入する、Viteプラグイン。RelaxVersionerの、TypeScript/NPM用的な位置づけ。バージョン計算アルゴリズムがRelaxVersionerと同一なので、両方を一元管理する場合は、Gitだけでバージョン管理できるので特に重宝する。名前が不吉なのは洒落だと思って忘れてください。
  • dir4json: ディレクトリ内のファイル情報をJSONで生成するやつ。dir2jsonがイマイチだったので2番煎じ。
  • typed-message: Reactアプリケーションのメッセージの多国語化。これもn番煎じだけど、これのアドバンテージは定義体のシンボルがタイプセーフになること。Viteプラグイン。
  • tar-vern: tarの他の実装の公開インターフェイスがイマイチだったので作った。screw-upで、NPMパッケージの操作に使用。
  • scheme-cd-ripper: CDのリッピングソフトウェア。CLI。Ubuntu/Debian向けのネイティブ実装。アルバムアート画像をAAで表示できるのが最大の売り(?)(今度登壇して解説)。
  • screw-up-native: screw-upが思いのほか良いツールに仕上がったので、将来を見越してネイティブ版を実装。C言語のみで実装。
  • ga_runner: GitHub Actionsで、イミュータブルビルドをプライベートランナーで実現するためのスクリプトセット。内部はpodmanでのコンテナリサイクル、GAのアップデートに自動追従など、GAのホスティングと同じような感じでランナーを使えるようにすることを目標にしたけど、まだちょっと詰めが甘いかもしれない。

あとはまだ完成が遠いものがいくつかあります(上に挙げたものは基本的に完成、もしくはほぼ完成のものです)。 数が多いので全部細かく紹介出来ませんが、いくつかはそのうちここで取り上げるかもしれません。

それではまた。

About

Kouji Matsui

  • Full name: Kouji Matsui.
  • kekyo, けきょ, kozy_kekyoというクレジットを使うことがあります。
  • 愛知県の付近のモグリで引き籠モラー。
  • 自転車乗りです(最近忙しすぎて乗れてない)
  • Center CLR主催しています。
  • バックエンドシステム・言語処理系とそのツールチェイン・ライブラリインターフェイス設計、などが主戦場/興味の中心。
  • 最近はTypeScript、またはC/C++、ハードウェアデザイン(コンピューター回路設計)辺り。 TypeScriptやってるとUIerと間違われそうだけど、専門領域は違います。 成果物はだいたい公開しているので、GitHubを参照してください。
  • 赤い色は、某WP 1520由来。今ではマイカラー。燃える赤 🔥

Links

おことわり

  • 本サイトではGoogle Analyticsによる統計を参照しています。 この情報は、私がこのサイト内のアクセス状況を見て、自分でご満悦、あるいは失望するためだけに把握します。 また、閲覧者が何らかのツールでこのトラフィックをブロックしても全く問題ありません。
  • ページに含まれるリンクには、Amazonのアフィリエイトリンクが含まれる可能性があります。ご了承ください。一杯おごるつもりで踏んで貰えると嬉しいです。