tsutsuiの作業記録置き場

NetBSDとかPC-6001とかの作業記録のうち、Twitterの140字では収まらない内容や記事としてまとめるべき内容をとりあえず置いてみる予定

NetBSDコミットログで振り返る2021年

かつては「自宅から車で行けるOSCには出る」という方針でイベント駆動で NetBSD謎マシン展示をしていたのですが、2020年1月のOSC大阪を最後に物理OSC開催がなくなってしまい、2021年はついに一度もOSCその他のNetBSDマシン展示発表をしない年になってしまいました。10月以降はmikutterにどっぷりでしたが

じゃあ昨年はNetBSD的に何をやっていたのか、ということで、 NetBSD CVSリポジトリのコミットログメッセージが流れてくる source-changes の mailing list の情報をベースに、2021年のNetBSD活動を振り返ってみようと思います。

2021年のコミット数

source-changes の mailing list はそのままでは集計や検索がしづらいので、 NetBSD developer の ryo@ さんがメンテしている NetBSD Source-Changes commit log search から情報を拾ってみると、2021年に自分のコミット数は 86回 でした。

これが多いのか少ないのかと考えると少なくはないのでしょうが、2020年は NetBSD/sun3 の Xorg サーバー実装とか MIPS Windows NTマシン Express5800/230 の電源修理からの NetBSD/arc 復活とか フランスから送られてきた HP9000/300 フレームバッファXサーバー用ビットマップオペレーション実装とか自分的に満足度の高い作業をやっていた反動で、2021年はなんとなく地味に感じてしまっていたのでした。いろいろやってるのにそれを展示してないからなおさらか

とはいえ、改めて 2021年の自分のコミットを見ていると、それなりに力を入れて書いたものもあります。そういうものをいくつかピックアップしてみました。

NetBSD/x68k Xサーバー 3ボタンマウスエミュレーション

まず 2021年2月7日コミットのこれ。

X68030の純正マウスは2ボタンなのですが、過去に自分が X11R7対応したXサーバーでは真ん中ボタンのいわゆる3ボタンエミュレーション(左右ボタン同時押しで中ボタンが押された扱いになる)が実装されていませんでした。

これ自体はだいぶ前から気になっていたのですが、2020年秋ごろからサーベイ自体はしていて、「各Xサーバー共通で使える3ボタンエミュレーション実装はない」ということはわかっていました。実態としては、旧XFree86ベースのxf86-input-mouseドライバLinux evdev用ドライバWindows Xサーバー実装の Xwin それぞれで別の実装がされている状態でした。

それぞれがまた歴史が積み重なった実装で読むのが大変なのですが、最終的には状態遷移をきっちり設計してあるっぽい xf86-input-mouse のマウスドライバ実装の状態遷移部分を他のインターフェースから分離する形で持ってきて、 NetBSD/x68k に限らずなるべく汎用な形で流用ができるインタフェース設計と「読めばわかる」構造体その他の実装定義を考えた上で NetBSD/x68k サーバーに組み込んだ形でコミットしました。

「左右同時押しで中ボタン扱い」という要件自体は簡単なように見えるのですが、「同時押しといってもどれだけのズレを許容するのか」「同時押しを待っている間に離されたらどうするのか」「同時押しの後に片方だけ離したらどうするのか」「中ボタンのダブルクリックは可能なのか」と言ったことを考え出すと結構難しい設計が求められます。

また、マウスのイベント自体も、PS/2やバスマウス、各種ワークステーションで定義が異なる、OSのデバイスドライバごとでも定義が異なる、Xサーバー側プロトコルも明示的なドキュメントがない、という混沌状態で、「コードから定義を推測する」という練習にもよいかと思いました。

なお、最初は X68030 エミュレータである XM6i 上でテストをしたところまったく意図通りに動かず調べるのに苦労したのですが、「ホストである NetBSD/i386 側の Xサーバーでも 3ボタンエミュレーションが有効になっているため先にそちらにイベントを取られてしまっている」というしょうもない罠にハマりました。3ボタンエミュレーションを無効にするには /etc/X11/xorg.conf のマウス以外の各セクションをすべて書く必要があり、これも結構面倒でした……。

NetBSD/m68k カーネルスタックオーバーフロー問題修正

次に結構苦労したのが、この 2021年2月23日の NetBSD/m68k カーネル全般のスタックリークの修正。

前述の 2020年の NetBSD/sun3用 X11R7サーバーを動かしていると「おもむろにカーネルスタックオーバーフローっぽい症状で panicする」という現象が起きていました。

ただ、カーネルスタックが壊れているためにバックトレースが取れないという問題がありました。カーネルスタックを2倍以上に増やしても発生するため「単なるスタック不足ではない」ということは見えていたのですが、そもそも「スタックがリークする」という現象がどういうシナリオで起こり得るのかがずっとわからないままでした。

その後、 NetBSD/x68k の3ボタンマウス修正に伴うカーネルクラッシュダンプ修正の作業を経て、前項の NetBSD/x68kサーバーでも起きていたカーネルハングアップの現象が NetBSD/sun3 のスタックオーバーフローと同じ現象らしい、ということがわかってきました。

XM6i上でも再現するということがわかったので、ひたすら頭悪く実行トレースを取るという手法で調査を進めました。

MC68030マニュアルや各種実装と突き合わせた結果、「シグナルハンドラ関連でコンテキストを切り替える際に m68kのトラップ要因によってはカーネルスタックをずらす必要があるがその処理が抜けている」らしい、という仮説にたどり着きました。

このあたりのツイートを拾い出すとエンドレス Togetter セルフまとめになるので詳細はまた別途

確信には至らない状態でバグレポとして投げたところ、 NetBSD開発者の thorpej氏からも「その修正で妥当」というコメントもいただくことができました。

こうやって結果が見えてくると理屈もわかるのですが、メモリフォールトのトラップがトリガであるため「メモリが極端に少ないマシン」で「激しくページアウトするような負荷をかけた場合」にしか起きないこともあり、結果としてこのバグは10年以上も残ったままになっていたのでした。今でも m68kマシンを「使っている」ような人はたいてい MC68060でメモリフル実装だったりする

いずれにせよ、これで X68030 や sun3/60での Xサーバー展示デモにおける「そもそもXを起動すると落ちる」という問題が一つ解消したはずなので、早く物理OSCが復活してほしいところです。

なお「目スタックトレース」という作業は一部の方には異様なものとして映った模様。慣れればそんなに大変じゃないんですけどね

HP9000/300 HP-IBディスクサポート改善

これはカーネルの改善というよりは実ハードウェアのテスト作業がメインでした。

HP-IBというよりも GP-IB あるいは IEEE 488 といったほうが通じやすいかと思いますが、もともとは HPが自社計測機用に作ったバスプロトコルおよび物理層の規格が HP-IBです。計測機用規格といいつつ、パソコン用にディスクドライブやプリンタなども接続可能で、 NetBSD/hp300 でもそれらのデバイスのサポートが入っています。

私自身は 2001年頃に NetBSD/hp300 を動かし始めてからいろいろあって今では NetBSD/hp300 の機種担当という立場にあるのですが、 2020年まで一度も HP-IBのデバイスNetBSD/hp300でテストしたことがないまま、という状況でした。

HPDrive

機種担当であるにも関わらず HP-IBの動作がずっと未確認というのはよろしくないなと思っていて HP-IB関連情報を過去にサーベイした際に、Windows PC + HP-IBインターフェースの組み合わせで HP-IBバイスを模擬する HPDrive というのものを見つけていました。

www.hp9845.net

この HPDriveで使える National Instruments製 PCI GP-IBインターフェースボードがヤフオクで手頃な値段で出ているのを見かけたのが 2020年の年末。

その後の GP-IB用ケーブル手配その他を経て、無事に NetBSD/hp300 で動作確認できたというの以下の 2021年1月9日のツイート。

とはいえ、このときは「何もしなくても動いた」という状態だったので、特にこれといったコミットはありませんでした。

HPDisk

HP-IB関連サーベイの中では、前述の HPDrive の他に「PICマイコンとSDカードで HP-IBディスクを模擬する」という HPDisk というものも見つけていました。

www.dalton.ax

別PC設定というひと手間が必要な HPDriveと違い実デバイスと同じようにつなぐだけで使えるこちらもいいなと思ってはいたのですが、配線基板を起こす、もしくは入手する手間を考えると「すぐに試してみる」というわけにも行かず、いったん保留にしていたのでした。

そんな過去経緯のあと 2021年4月上旬になって、この HPDiskの作者の方から「HPDiskのテストのために HP9000/340 に入れた NetBSD/hp300 が起動しないのだけれど」という相談を受けました。この起動問題自体についてはざっと調べて「古いモノクロフレームバッファの probeルーチンで存在しないカラーパレットをアクセスしているため」という既知の問題が放置されていただけというのがわかり、修正して無事に起動するようになりました。

このやり取りの中で「自分でハンダ付け工作できるなら基板送るよ」と言われて「ぜひぜひ」とお願いして部品発注その他を経て作り出したのが 2021年5月。

じわじわ始まっていた半導体需要逼迫の影響で PICマイコンの手配に手間取りましたが、PICライタも入手して動かし始めたのが5月連休明けくらい。

これも個別作業のツイート引用をしだすと終わらなくなるので詳細は別の機会に回しますが、「NetBSD/hp300ほど HP-IBプロトコルを強烈に使う testsuit はない」というコメントをもらう状況で、こちらからデバッグログを送っては更新版の Firmwareを送ってもらう、ということを10回近く繰り返すこととなりました。

最終的には 2021年7月8日の Firmware v0.20で NetBSD/hp300 でも HPDisk の一通りの機能が動くようになりました。この作業の中で、従来の NetBSD/hp300で正しくサポートされていなかった「複数スレーブデバイスディスクのサポート」についても HPDisk側で任意のドライブを指定できるように改善してもらったため、別途教えてもらっていた OpenBSD/hp300 の修正も合わせて持ってくることにしました。

コミット内容としては微妙に地味だったりするのですが、上述のように NetBSD/hp300に対する問い合わせがきっかけで久しぶりにハードウェア工作をしたり実機デバッグをしたりという交流を含めて楽しく作業をさせてもらいました。

なお、HPDiskの作者の方にもこの更新の件を取り上げていただいていますのでこちらもどうぞ。

www.elektormagazine.com

LUNAキーボードのCAP/かなLEDおよびブザーコマンドサポート

最後は9月から10月にかけて作業したこれら OMRON LUNAの NetBSD/luna68k キーボードドライバ関連のコミットです。

マイナーすぎるにもほどがある と言われそうですが、きっかけは LUNAエミュレータnonoの作者の一人である @moveccr さんのこのレポート。

LUNAキーボード マウスON/OFFコマンド

この問題を説明しだすとまた記事が長くなってしまうのですが、ざっと書くと
「LUNAのキーボードとマウスは同じシリアル通信ラインで信号が送られる」
「マウス信号のデータはキーボード送信データ 0x00〜0xFF のバイトデータのうち 0x80〜0x87 のデータをヘッダマークとして使ってボタン情報と移動量情報を送る」
NetBSD/luna68kカーネルはコンフィグでマウスが無効だとマウスデータの処理を行わない」
の3つを合わせると
「(マウスを使わない)ブートローダーとマウス無効のカーネルでマウスを動かすとマウスデータをキーデータと誤認する」
という問題です。

LUNAキーボードの仕様については過去に仕様書を見てざっと把握していたので「そう言われればそうだよな」と思いつつ実機でテストしてみたのですが、どうも再現しません。

この原因は
「LUNAのキーボードにはマウスON/OFFコマンドがある」
「実キーボードは起動時にマウスON状態の仕様だが、LUNA実機のROMは起動後にマウスOFFコマンドを送っている」
NetBSD/luna68kのカーネルも起動時にマウスOFFコマンドを送っていて /dev/wsmouseopen(2) するときにマウスONコマンドを送っている」
エミュレータであるnonoではマウスON/OFFコマンドが未実装」
ということでした。

ただ、実機でも起動後にキーボードを抜き差しした場合は同じ現象が再現します。

というわけでこの修正をしたのが冒頭のコミット5件のうち最初のもの。

CAP/かなLEDコマンドとブザーコマンド

この「マウスON/OFFコマンド」の修正と見出しの「キーボードのCAP/かなLEDおよびブザーコマンド」に何の関係があるかというと、「LUNA本体→キーボードにシリアルデータとしてコマンドを送信する」という処理の部分です。

この問題が持ち上がった 2021年9月時点の NetBSD/luna68kカーネルでは、LUNAのキーボードの仕様として定義されている機能のうち、キーボードLEDコマンドの制御とブザーコマンドの制御については未実装で、マウスON/OFFコマンドのみが暫定に近い形で実装されているのみでした。

マウスON/OFFのコマンドの実装としては「カーネルが起動時にマウスOFF」「デバイスの open 時にマウスON、 close時にマウスOFF」「同時にオープンできるデバイスは1つのみ」というものだったので、同時に複数のコマンド送信要求が発生することはありません。このため、マウスON/OFFのコマンドはシリアル通信ICのuPD7201のレジスタを調停無しで直接アクセスする実装になっていました。

マウスコマンド以外のLEDコマンドやブザーコマンドはユーザーランドプロセスからの入力をトリガにシステムコールで呼ばれる場合があります。また、CAPキー入力とブザー出力とが同時に発生するケースもあるため、これらを適切に処理するためには「デバイスドライバ側で送信データキューを用意してソフトウェア割り込み等を用いて送信キューデータを順次送信する」といった実装が必要になります。

この送信キュー処理については NetBSD/sgimips の R3000系マシンの実装を参考にサクッと(?)記述しました。

wscons(4) の LED・ブザー実装

詳細実装以前のインターフェース設計の問題として、NetBSDの MI (Machine Independent つまり各機種共通仕様) のスクリーン+キーボードのコンソールドライバである wscons(4) の仕様として、キーボードLEDのON/OFFやブザー出力要求がどのような経路でデバイスドライバまで到達するのかも調べる必要がありました。

調査においては、もともと wscons の仕様を説明した文書がほとんどないこと、LEDやブザーをキーボード側にコマンドとして送信する機種(主に NetBSD/alpha, NetBSD/pmax, NetBSD/sparc, NetBSD/sgimips といった Sun, DEC, SGIといったワークステーション)でドライバをちゃんと実装したものがほぼなかったことから NetBSDsrc/sys/dev/wscons および各機種のコードをかなり読み込むハメになりました。

またセルフまとめ状態で長くなってしまいますが、調査の過程についてはTwitterで適当にメモっていたのでそのまま引用します。

とりあえずでユーザーランドプロセスからの要求に対しては以下のように対処すれば良いということはわかったのでこれで実装。

  • CapsLockその他のLEDについては、デバイス固有の処理として WSKBDIO_SETLEDS (と WSKBDIO_GETLEDS) の ioctl(2) だけを実装すれば良い (それより上位は wscons(4) 側で対処される)
  • ブザーについては、デバイス固有の処理として WSKBDIO_COMPLEXBELL の ioctl(2) だけを実装すれば良い (それ以外は wscons(4) 側で対処される)

この修正が冒頭の5コミットのうち2つ目の修正。

LUNAキーボード CAP/かなキー挙動対応

前項のLED調査の過程で LUNAの CAPキー および かなキー の動作は
「キーを押してLEDが点灯するときに PRESSイベントを送信」
「キーを押してLEDが消灯するときにRELEASEイベントを送信」
「キーを離したときはイベントを送信しない」
という「いにしえのメカニカルロックキーをエミュレーション(?)した動作」であることが改めて確認されました。PS/2やUSBではこれらの modifier キーについても物理イベントは他のキーと共通(なのでドライバレベルでの入れ替えも可能)

NetBSD/luna68k カーネルではこれに対応しておらず CAPキーの操作とLED状態とCapsLock状態が一致しないという問題があったため、今回合わせて修正しました。これが3つ目のコミット。

また、nonoのキーボードエミュレーション実装の挙動が実機と異なるようだったので報告。これは nonoさんの次のバージョンで修正されることになりました。

uPD7201 sio(4) ドライバ整理

このあたりの uPD7201シリアルドライバの実装を見ていると、過去から気になっていた記述が思い出されて気になってしまいました。せっかくだから、と過去経緯を含めて調べて「コードを読めば何をしているかわかる」という方向にすべくいろいろ調査した上で見直しを検討。

このあたりを整理してコミットしたのが4つ目のコミット。

カーネルメッセージ出力とコンソールドライバ実装

この uPD7201ドライバの整理と並行して考える必要があったのが「ユーザーランドプロセスだけではなく、カーネル内 printf(9) によるコンソール処理についてもLEDとブザーの処理が必要」という部分。

シリアルコンソールの場合、 LEDの処理はなく、ブザーについても単にブザーに相当する ASCII制御コードを送信するだけです。よってカーネル側 printf(9) についても特別な処理はなく、 cnputc(9) 相当の関数でデータを送信しているだけです。

一方、キーボードコンソールの場合は LED制御もブザー吹鳴もキーボード側にコマンドを送信する必要があるものの、一般的にカーネル printf(9) からの呼び出しでは割り込みが使えません。よって、ユーザーランド側処理と同様の送信キューによる実装では対応が難しいという問題があります。

真面目に実装しようとすると、ストレージデバイスのように「割り込み使用不可の場合のポーリング処理実装」を入れる必要がありますが、超マイナー機種、かつ、使用頻度が高いとは言えないキーボードの 1〜3バイトのコマンドのためだけにどこまで工数をかけるのか、というのが悩みどころでした。

このあたりも細かく説明すると終わらないので、セルフまとめツイートで雰囲気だけ感じてもらえればと思います。

やはり無駄に長い引用になってしまう

最終的な妥協案としては、既存の cnpollc(9) の仕組みを使ってカーネル cngetc(9) からの呼び出しかどうかを判定した上で、カーネル内 cngetc(9) の場合は送信キューを使わずに従来のマウスON/OFFコマンド同様に直接 uPD7201のレジスタを操作してコマンドを送信する実装としました。

これが最後の5つ目のコミット。

まとめ

こうやって改めて思い出してみると、それぞれの作業で結構頭を使って考えていたのかな、とは思わないでもないですね。

「OSCがなくなったのでイベント駆動がなくなったと言いながらこれらの作業はどうなんだ」と言われてしまいそうですが、x68k Xサーバーの3ボタンエミュレーションを除くと「ユーザーの見た目にはほとんど現れないが内部的なあるべきハードウェア対応実装設計およびインターフェース設計」という感じで、このたぐいの「詳細実装設計メインの面白さ」をOSCネタで表現できたかというと微妙です。どんなもんでしょう

2022年以降もOSC含めた世情がどうなっていくのか見えていませんが、これまでと同じようなペースでぼちぼち NetBSDをいじっていけたらなあ、と思っています。今回のエントリで書いた内容に限らず「こんな中身の話を聞いてみたい!」というご意見があれば聞かせていただけると幸いです。

mikutter GTK3対応(4) 〜そして 5.0リリースへ〜

次回のエントリでは、この debian ruby-gtk2 パッケージのツイートを発端とした「mikutter GTK3対応の最終ステージ」について書いてみたいと思います。なんとか 12/25 の mikutter 5.0 リリースに間に合わせたい

mikutter GTK3対応(3) 〜miracle painter〜 - tsutsuiの作業記録置き場

mikutter.hatenablog.com

f:id:tsutsuii:20211225133011p:plain

というわけで無事にておくれてしまいましたが、まだ慌てるような時間じゃない、ということで前回の続きを書いてみたいと思います。

ruby-gnome GTK2廃止対応

前回のエントリで記載したとおり、 2021/4/22 リリースの ruby-gnome 3.4.4 で gtk2 のサポートは廃止され、当然ながら rubygems.org での gtk2 gem 更新も 3.4.3 までとなりました。

mikutter GTK3対応の前段階として、私が担当している pkgsrc の mikutter とそのための ruby gtk2 パッケージ の対応についてメモしておきたいと思います。

Farewell ruby-gtk2

Twitterその他をご覧の方はご存知と思いますが、私自身がメンテナとなっている mikutterおよび依存パッケージを含め、自分自身が必要な pkgsrc の各パッケージの検証を兼ねて NetBSD/amd64,i386 9.2 ておくれLive Image というものを NetBSD本体のリリースや pkgsrc の4半期毎リリースに合わせて更新するようにしています。

その関係で IFTTT で自分担当の各パッケージのリリース情報を見ていたため、 ruby-gnome 3.4.4 のリリースとその中の GTK2削除についてはその日のうちに気づいていました。

ただ、 mikutter本体の GTK3対応作業がしばらく止まっていたこと、 pkgsrc的に mikutter 以外で ruby-gnome を使っているのは ruby-gnomeメンテナご本人によるプレゼンツール rabbit くらいだったこと、さらに私自身が pkgsrc ruby-gnome パッケージの担当だったこともあり、「当面は現状の ruby-gnome 3.4.2 のままにするか」と、いったん様子見とすることにしました。

NetBSD 9.2 リリースにあわせて作った 20210521版の Live Image でも、「ruby-gnome をどうすべか」と思いつつ、リリースアナウンスや変更履歴では特に触れていませんでした。

更新プレッシャー

2021年7月の pkgsrc-2021Q2 リリース後の Live Image更新については Firefox ビルドのための rust のビルドが NetBSD/i386 では失敗する問題のためにしばらく保留していました。

8月に入ったところでこの問題が解決して Firefoxもビルドできるようになり、 Live Image更新作業を始めたところで「そろそろ ruby-gnome 対応を真面目に考えるか」と、いったん GitHub issue でメモすることにしました。

方針としては以下のような感じです。

ruby-gnome では 3.4.3 → 3.4.4 の更新で ruby-gtk2 が削除された。
mikutter は未だ gtk2 対応のみで gtk3 対応は道半ばなので
pkgsrc として対応を考える必要がある。

  • pkgsrc/x11/ruby-gtk2 は 3.4.3 のまま残す
  • pkgsrc/meta-pkgs/ruby-gnome から ruby-gtk2 を外して、 ruby-gtk2 側でバージョンや依存関係を個別定義する
  • もし 3.4.4 以降から現状の 3.4.9 までに API 変更がある場合は ruby-gtk2 のソースパッチで対応できるか検証する
ruby-gtk2 削除対応 · Issue #38 · tsutsui/netbsd-teokureliveimage · GitHub

単純に GTK2 gem を古いままにして GLIB2 その他の gem を新しくしてしまうと以下のような懸念がありました。

ただ、悩んだところで他に手はなく、検討のために ruby-gnome 3.4.4 から 3.4.9 までの変更をざっと確認したところでは virtual functionサポート追加以外のあからさまな API/ABI変更は無さそうで、とりあえず試したところ GTK2版 mikutter は動くようでした。

このため、  ruby-gnome 3.4.9 への更新と合わせて上記の変更を pkgsrc にコミットしました。これが 2021年8月29日です。

パッケージ更新問題

という具合いに「どうしたものか」と考えた中で流れてきたのが、前回も紹介した以下のツイートです。

同じパッケージ管理者として debian ではどうなっていくのだろうか、ということでこれ幸いといろいろお話を聞いてみることに。

pkgsrc では ruby-gnome の各パッケージを独立した gem から生成するように変更していましたが、 debian では単一の ruby-gnome の GitHub リポジトリを親として各パッケージを生成しているそうです。このため、 ruby-gtk2 のみを古いまま新たなパッケージとして追加することは難しいのではないか、ということでした。

やはりておくれる運命のmikutter

GTK3再始動

この私の勝手な決意表明(?)のツイートを見て立ち上がってくれたのが、人気Android Twitter/MastodonクライアントYukari の作者で mikutterのコミッタでもある shibafu528氏です。

ネクロマンシーは、死体による占い全般を指す通俗的な呼称で、未来や過去を知るために死者を呼び出し、また情報を得るために一時的な生命を与えることを含む。

ネクロマンシー - Wikipedia

2021年5月の ruby-gnome 3.4.5 で virtual function サポートが入ったものの、 mikutter GTK3作業をしていた yuntan_t氏は多忙となったため作業は止まっていた状態でした。まずはこの virtual function 対応を確認するところからだったようです。

というわけで、 9月7日 22:27の決意表明ツイートからわずか 2時間ほどで shibafu528さんがジェバンニしてくれたのでした。

ここから mikutter GTK3対応が再始動していくことになります。なんでも書いてみるものです

ruby-gnome側課題

まず ruby-gnome 3.4.5 での virtual function に問題があったようです。これは、 shibafu528氏により pull request が出され、すぐにマージされることとなりました。

github.com

このスーパークラスの virtual function の問題のほかに、 ruby-gnome 側の問題としてもう一つあったのが mikutter GTK3対応(1) のエントリでも記載した pango gem のモージュル名変更の問題です。

この問題については、12月頭の上記のブログエントリを見た shibafu528さんがサクッと mikutter側にモンキーパッチを用意してくれました。

  1. https://github.com/ruby-gnome/ruby-gnome/pull/1433 をモンキーパッチで適用
  2. ruby-gnome 3.5.0 では PangoCairo 配下にある定数群が ruby-gnome 3.4.9以下では Pango 配下にあるので、エイリアスを張った
提案 #1551: gtk3: ruby-gnome 3.4.9でも動くようにする - mikutter - やること

mikutter 5.0.0 リリースの今日 2021年12月25日まで ruby-gnome 3.5.0 のリリースは行われなかったため、現時点ではこの暫定対応がそのまま採用されている状態となっています。

mikutter GTK3 cleanup

9月10日以降、主に shibafu528氏が中心となって GTK3版の実用を目指したデバッグが始まりました。詳細は mikutter Redmine の活動ページを見ていただくとして、大枠は以下のGTK3チケットにある項目の対応です。

各チケットの件名を見ればだいたい雰囲気はわかるかと思いますが、 miracle painter というフレームワーク対応が概ね終わり、実用するための修正がメインになっていることはわかるかと思います。

9月終わりになると toshi_aさんも加わって GTK3版 miracle painter 詳細実装の修正も進んでいきます。

GTK3作業参戦

shibafu528氏と toshi_aさんが GTK3版の修正に取り掛かっていた9月後半、私自身は NetBSD/luna68k キーボードシリアルドライバの修正などというマイナー作業を2週間ほど続けていました。

10月に入り pkgsrc-2021Q3リリースに対応した Live Imageの更新版作成作業に取り掛かったところで、例の ruby-gnome 対応 GitHub issue を思い出し、「そろそろやるか」と自分でも手を出してみることにしました。

前述の PangoCairoモジュール名問題や pqueue gem 問題等々を対処しつつもちょくちょくクラッシュが発生してちょっと心を折られかけたのですが、ここでもアシストしてくれたのは shibafu528氏です。私が作っているておくれ Live Imageを使って NetBSDでの動作検証をしてくれました。

とりあえず NetBSD+pkgsrc 環境固有の問題はないということはわかったので、むりやりで ruby-gnome パッケージ関連のファイルを GitHub HEADのファイルで上書きして作業をすることにしました。その上で、各種 Twitterプラグインを持ってきて mikutter GTK3対応(1)のエントリ記載の通り適当に GTK3対処を入れると、とりあえずタイムラインは出るようになりました。

mikutter関連作業をするときは関係者が多い都合上 Mastodonメインになるのですが、とりあえず「ruby-gnome 3.5.0 出ないかなー」ということで Twitterにもそれっぽく告知したのがこのタイミング。

Twitterプラグイン GTK3対応

Twitter プラグインのうち、ホームタイムライン表示関連のプラグインはとりあえず動いていたのですが、いくつかのプラグインはそもそもロードされない、ロードされても動きがおかしい、という状況でした。

GTK3対応の中心となっていた shibafu528氏や toshi_aさんは Twitter プラグインのサポートにあまり前向きではないというのは察せられていました。これは自分でなんとかするしかないか、と ruby の勉強も兼ねて Twitter関連プラグインの GTK3対応作業に本腰を入れてみることにしました。

個別の作業を書き出すといくらページがあっても足りないので適当に端折りますが、やはりというかなんというか ruby や miracle painter 以前に GTK2→GTK3の移行作業の勘所を掴むというところが一番苦労しました。

GTK3対応の作業としては、大きく以下に分けられるかと思います。

  • GTK2→GTK3 における API変更
  • GTK2 と GTK3 の同一ウィジェットのデフォルトの振る舞いの変更
  • ruby-gnome の gtk2 gem と gtk3 gem の非互換性
  • mikutter GTK3版本体の変更に伴うプラグイン側の変更
GTK2→GTK3 における API変更

まず、すぐに気づくのが Gtk::Hbox, Gtk::Vbox が deprecated という警告。

ただの警告なので無視してもよいのですが、結構大量に出るのでこれを消さないと本題のデバッグがはかどらないという問題が。まあ、これらは警告メッセージ通り Gtk::Box(:horizontal)gtk::Box(:vertical)機械的に置き換えれば済むのでまだ楽な部類です。

次に :draw シグナルの廃止。

これは最初見様見真似で :expose_event に置き換えたのですが、実は GTK本家のドキュメントにも記載があるとのことでした。まあ読んでも中身の本質はわからないんですけど

あと、数は少ないものの困ったのが色関係の指定。

GTK2では #FFFFFF 形式の 24ビット表記だったものが、 GTK3では各色 0.0〜1.0 の数値を指定する CSS方式に変更になっているようです。これが機械的に 1:1 で置き換える作業では対応できないようで、この問題に当たった user_detail_view プラグインでは mikutter 本体の gtk_postbox での修正内容を適当に真似る、という雑対処で済ませています。

他にも細かい修正があったような気はしますが、これらは地道に対処していく類の作業になります。

GTK2 と GTK3 の同一ウィジェットのデフォルトの振る舞いの変更

一番手を焼いたのがこれです。エラーも警告も出ずに表示だけがおかしいので、どう修正すればよいか見当がつかない、検索しようにも良いキーワードが思いつかない、できることは試行錯誤だけ、という状況に陥りがち。しかも何度も起動するには mikutterさんが重い

まず Gtk::ScrolledWindow の幅指定。

画像投稿プラグインの mikutter-uwm-hommage で謎の表示になったのがこれ。 GTK2環境ではうまいこと(?)親のウィジェット幅に収まっていたものが、謎のデフォルト値 200pxくらいの幅になってしまうようでした。最初は width_request = 500 を指定したりしたのですが、結局 propagate_natural_width = true を指定するとそれっぽくなるようでした。

次に、かなりの時間を費やしたのが Gtk::BoxGtk::Scrollbar の組み合わせ挙動。

GTK2環境ではこの組み合わせで Box表示が表示領域より大きくなるとスクロールバーが表示されていたが、 GTK3ではスクロールバーが表示されるだけで機能しないようでした。それだけではなく、「表示領域のサイズが収まるようにアプリケーションのウインドウサイズが無限に拡大していく」という動作を引き起こし、当該操作のあるタブを表示してしまうと mikutterさんがあっという間にフルスクリーンサイズに張り付いてしまい、アプリごと落とすしか無いという状況に陥っていました。

何日かトライアンドエラーを繰り返して、「Gtk::Scrollbar を捨てて Gtk::ScrolledWindow を使えばよい」という結論に。

他に目についたのは以下の Gtk::ImaegMenuItem の挙動ですが、詳細はそれぞれ Redmine チケットを参照してください。

ruby-gnome の gtk2 gem と gtk3 gem の非互換性

これはとりあえず Gtk::TreeViewvisible_range1件だけでしたが、とにかくドキュメントがないため、試行錯誤のテストコードを書いて挙動を確認しながらの修正になりました。

visible_range が動かないのは gtk3 の問題なのか gem の問題なのかはよくわかりませんが。

添付 ruby-gtk3-TreeView.rb のテキトーテストプログラムを書いていろいろ調べたところ、
どうも GTK2 と GTK3 とで visible_range の返り値が異なるようです。
具体的には GTK3 では 1つ目の返り値として bool が返ってきています。
GTK2 での返り値 1つ目と2つ目 GTK3 での返り値 1つ目と2つ目

ドキュメントがないので仕様がよくわかりませんが、とりあえずブラインドで
1つ目の返り値を visible_range の有効無効判定に使い
2つ目、3つ目の返り値を従来の start_path end_path 相当で扱うように書き換えると
一応ユーザーフォロータブの表示動作(スクロールして表示されたところだけアイコンを出す)は
mikutter_gtk3-UserList-visible_range.mp4 のとおりで実現できているようです。

バグ #1514: gtk3: core/mui/gtk_userlist.rb で visible_range と :expose_event が参照されている - mikutter - やること

GIで非voidな戻り値型かつout引数を伴う関数呼出の戻り値は、関数の本来の戻り値+out引数の配列。
今回は関係ないですが、out引数が1つの場合には配列ではなくそのout引数を単体で返すといった特殊ケースも存在するようです。
(さすがにこの辺の規約はドキュメントあってほしい)

バグ #1514: gtk3: core/mui/gtk_userlist.rb で visible_range と :expose_event が参照されている - mikutter - やること
mikutter GTK3版本体の変更に伴うプラグイン側の変更

mikutter の GTK3対応においては、 GTK APIを 1:1 で置き換えたのではなく、別のウィジェットAPIに置き換えたケースが複数あります。よくハマったところとしては、ウィジェット配置が Gtk::Box から Gtk::Grid に置き換えられた部分をバンドルプラグインで参照しているようなケースです。また、置き換え作業やリファクタリングにおいて意図しない変更が入ってしまった部分もあるようでした。

これらはわかってしまえば機械的な置き換えになるのですが、前回のエントリで書いたとおり GTK3対応は yuntan_t氏が長期に渡って独力で作業していたこともあり、記録を掘り返しながらの作業になりました。

個別の内容についてはそれぞれ Redmineチケットに挙がっていますので、今後 mikutter 5.0 のバグ取りに参加しようという方はながめてみてください。

miracle painter とセットで実装されている timeline 関連の変更も大きく、挙動が異なる場合の差分確認が結構大変だったという印象です。ただ、これらの作業記録が将来の移行作業でも役に立つことを信じたいと思います。

その他、雑多なメモトゥートだけ貼っておきます。

mikutter GTK3対応 まとめ

長々とセルフまとめ状態を含めて書いてしまいましたが、ここで書いた以外にも様々な活動を経て、 GTK3版 mikutter は無事に mikutter 5.0.0 としてリリースされることになりました。

冒頭で引用した mikutter 5.0 リリースのエントリの中で、 toshi_aさん自身は以下のように書かれています。

Gtk3対応は、はっきりいって退屈なものでした。 だからこそ、貢献してくれた方々に心から敬意を表します。

mikutter 5.0.0 - mikutter blog

しかし、NetBSDカーネルC言語以外のプログラミングに手を出す機会がなかなかなかった自分としては、10月から約2か月間で得られた経験値はある意味新鮮で大きなものでした。いい歳になってしまった自分ですが「古い常識に囚われた老害にならない」ためのよい勉強をさせてもらったという気持ちです。

今までも mikutter Redmine のモデレータとしてサポートメインの立場でお手伝いをさせていただいていましたが、今後は mikutter 本体コードへの貢献量も増やせればいいな、と考えています。

最後に、 mikutter というアプリを作り上げてメンテし続けてくれている toshi_aさん、そして yuntan_t氏、 shibafu528氏、その他 mikutter 開発に関わってくれた方々に感謝の言葉を贈りたいと思います。

このエントリを読んでいただいた方も、ここまでの長文駄文にお付き合いいただきどうもありがとうございました!

mikutter GTK3対応(3) 〜miracle painter〜

次回以降は、このあたりの歴史や実際の修正作業について背景の解説(多分に推測を含む)を含めて書いてみたいと思います。

mikutter GTK3対応(2) 〜サーベイ中〜 - tsutsuiの作業記録置き場

前回のエントリの続きで、 mikutter GTK3対応の歴史や実際の修正作業について、Redmineその他の資料サーベイ結果とともに背景の解説(多分に推測を含む)を書いてみたいと思います。

ruby-gnome 3.5.0

……と、いきなりですが本題の GTK3の歴史の前に、 ruby-gnome 本家に動きがあったので少しだけ。

日本時間の 12月18日朝に、変更履歴である NEWS ファイルに 3.5.0 の記載が追加されたようです。

github.com

すでに 2021/12/18 の日付が入っているようですが、 GitHub のコミット状況を見る限りではもう少し時間がかかりそうな雰囲気です。

一方、 pkgsrc に更新版の 3.5.0 対応をコミットするには全gem分の修正と動作確認という事前作業量が多いので、 pkgsrc-2021Q4 freeze 前に対応するには日程的にちょっと厳しい感じです。

そもそも本体の mikutter 5.0 GTK3対応版は 12月25日リリース予定なので、どのみち対応は pkgsrc-2021Q4 リリース後の freeze 期間明けになるかと思います。

mikutter 5.0.0-alpha1

このブログエントリ記事を書き上げるのが遅れているうちに、12月19日に mikutter 本体の方もリリーステスト版とも言うべき 5.0.0-alpha1 がリリースされています。

mikutter.hatenablog.com

ただ、特にバグ修正が一段落した版というわけではなく、12/25の本番(?)リリース作業のテストという位置付けです。よって、パッケージシステム等の管理者としてのテストでリリース版 tar ball が必要となる作業を除けば、ブログ記事本文にあるとおり gitで develop ブランチを取得してテストするほうがよいでしょう。

mikutter GTK3対応:その長い道のり

というわけで、ようやくですが本題の mikutter GTK3対応についてです。

GTK3対応の始まり

mikutterの GTK3対応は、今から5年前、2016年 9月10日付けで「gtk3」の Redmineチケットが登録されたのが始まりと言えます。

dev.mikutter.hachune.net

チケット履歴にある通り、当時京都で開かれたRubykaigi2016で GTK3対応関連の会話があったようで、かつてmikutterユーザ会@関東を開いたこともある Katsuyoshi氏が担当としてアサインされました。

ここでGTK3対応の前準備としての機械的なメソッドその他の置き換えが行われましたが、このときの Katsuyoshi氏の置き換え作業は 2016/9/13まで。その後、具体的にmikutterが GTK3で動き出すところまでには至らなかったようです。

yuntan_t氏によるGTK3対応着手

GTK3対応活動から 2年ほど経過した 2018年 8月25日になって、それまで mikutter Redmineでも積極的に活動されていた yuntan_t氏が GTK3対応に名乗りを上げてくれました。

こんにちは.gtk3対応に興味があったのでやってみています. https://github.com/yuntan/mikutter/tree/gtk3 に置いてます.topic/887-gtk3をmaster(557b03b)にrebaseしてその続きで作業しています.起動できるところまで作業を進めましたが,MiracrlePainterで苦戦しています.

機能 #887: gtk3 - mikutter - やること

この作業が toshi_aさんの目に止まり、 yuntan_t氏はめでたく mikutterのコミッタとなり、上記 miracle painter の課題解決に取り組んでくれました。

動作がかなり不安定(表示されたりされなかったりする)ですが,MiraclePainterを表示させることが出来ました.

https://i.gyazo.com/bc5c3bfdeb91ecc3440d358c828ed1b6.png

機能 #887: gtk3 - mikutter - やること

これらの yuntan_t氏のコメントから察せられるかと思いますが、 mikutter の GTK3対応の本質は、「単なるGTK2→GTK3の移行作業」というだけではなく、この「miracle painter」の対応の困難さにありました。

"miracle painter"

「miracle painterとはなんなのか?」ということを説明するには、 miracle painter が最初にmikutter に実装された mikutter 0.0.3.5 リリース前後の mikutter blogの記載を引用するのが早道です。関連するエントリをピックアップしてみます。

ブログエントリ その1

mikutter blogのこちらの記事が初出と思われます。

mikutter.hatenablog.com

ひっそりやってたんだけど思ったより拡散したので、解りづらい形式で説明します。
あまり説明に時間を食いたくないので簡単に。

cairoとは

すごくかわいくなります

mikutter cairo版とは

萌ゆいものとかわいいものが一緒になってやばいです

具体的に何をしたの?

タイムラインをリストビューにして、各セルをcairoでレンダリングしています。見た目はできるだけ今までと変わらないようにしています。
だから別にcairoを使ったことだけが新しいわけじゃないんだけど、それに気づいたときにはみんな知ってたのでておくれました。

何が良いのか

高速になって、メモリ消費が少なくなって、ライブラリのバグを踏む確率が下がることを期待しています

欠点は

とくになし。ただし、リプライのUIが若干変わってしまうかも。これは最終的に従来の方法を実現できる可能性がある。
それさえなければ欠点は特になさそう
cairo版と言われてるものについて - mikutter blog

わかる人にはわかる「これぞ toshi_aさん節」というエントリですが、ポイントは本文中でも言及されている「cairoでレンダリング」というところかと思います。

ブログエントリ その2

miracle painterについてもう少し詳細が記載されたのが次のエントリです。

mikutter.hatenablog.com

こんばんわ。先程新UIをリリースしました!何がどうなったのか簡単に説明するよ。
 
mikutterの従来のレンダリング方法
 
今までのmikutterは、ウィジェットを綺麗に並べてタイムラインを実現していました。この方法だと、あらゆるウィジェットを使用することができるので、タイムラインのレンダリングがとても自由にできます。
 
したがって、mikutterのタイムラインは他のTwitterクライアントのそれに比べてかなりリッチな部類だと思います。なかでも、アイコンオーバーボタン(アイコンにマウスカーソルを重ねるとボタンが出てくる機能)、ふぁぼられ、リツイート累積表示、リプライ宛先表示などのmikutterの代表的な機能は、この方法でタイムラインをレンダリングしたからこそ容易に達成できた、という背景がありました。
 
何が悪いのか
 
本来、ウィジェットは頻繁に追加削除されるものではないので(大体は作ったらそれでおしまいですよね)、ある程度タイムラインの流れが速いと、異常な速度でウィジェットの作成/削除/パッキングが行われます。
配置の自由度が高いぶん、こういった変化があったときの再描画のコストは馬鹿になりません。また、Ubuntuの最新版に入っているRubyGTKはとても古く(nattyには0.19.3が入っているが、最新は0.90.8)、大量のバグが含まれているのが現状です。そのようなAPIに頻繁にアクセスするので、ライブラリがSegmentation Faultしてmikutterが巻き添えになることがしばしばありました。
 
他のTwitterクライアントのレンダリング
 
mikutterは他のTwitterクライアントと比較して、レンダリングが飛び抜けて遅いですね。説明は上記の通りだけど、ではどうしてそんなことをしたかというと、現在のmikutterは、とりあえず表示できるものを仮に作った段階だったのです。つまり、mikutterのUIは新たにつくり直す必要がありました。
 
では、他のTwitterクライアントはどうしているかというと、リストビューを使用していると思われます。
リストビューは、パーツが縦に並ぶことを前提にしているのでどうやら速いらしい。ソートオーダーなどの細かい話もGtkがやってくれるのでRubyでやるよりは早くなりそう。
 
しかし、じゃあリストビューにすればいいじゃない、と簡単にはいかない。というのも、この中に置けるのは、せいぜい画像と文字くらいだからで、新しいRendererを作成するにしても、今までのように好きなウィジェットを思いのままに設置するとはいきません。
 
Cairo+リストビュー
じゃあ、全部画像にしたらよくね!?という奇想天外なことを思いつきました。画像だったらすきにレンダリングできるけれど、キャッシュすれば速さもでるんじゃねーのと思ったわけです。

リストビューは、任意のRendererを設定できるので、今回は実際にTLを描画するやつをMiraclePainterという名前にしました。TLの先まですべてミク色に染めていただきましょう!!!

で、リッチな表示ができないとか言ってたけど蓋を開けたら右のようになってます。ついでだから微妙にマイナーチェンジしましたが。これがどの程度速くなったかは、実際に起動してもらったほうがよさそうなのでここでは書かないけれど、

  • ボトルネックになってた部分は100倍くらいの効率のアップ
  • 起動時間は2秒
  • 新しいタブを作ったりした時にブラックアウトすることもなくなった
  • 入力中に固まってイライラすることもなくなった
  • RubyGtkのAPIを叩く回数が減ったので、Segmentation Faultの確率が減るかも!?

と、圧倒的な進化を遂げました。CPU使用率も大幅に減ったので、いままで遅くて使えんと言っていた人もつかえるようになるんじゃないかな。
新UIについて - mikutter blog

こちらも全開の toshi_aさん節ですが、ポイントは以下かと。

  • 全部画像にしたらよくね!?
    • 言い換えると、このエントリの本題であるGTK3対応の困難さを暗示している?
  • ソートオーダーなどの細かい話もGtkがやってくれる
    • 「全部画像」の一方、リストビュー表示についてはGTKに依存していることになる?
  • Ubuntuの最新版に入っているRubyGTKはとても古く
    • Ruby-gnome2 0.19.3」というキーワードが toshi_aさんのトラウマになっていた時期もありました
    • 「RubyGtkのAPIを叩く回数が減ったので、Segmentation Faultの確率が減るかも」というのも隔世の感
  • 今回は実際にTLを描画するやつをMiraclePainterという名前にしました。TLの先まですべてミク色に染めていただきましょう!!!
    • わかる人にはわかるとおり、「miracle painter」の語源は言わずと知れた以下の動画の曲ですね

実際の効果も相当なものでした。

当時の mikutter を思い返してみると、「ふぁぼられを表示する」「ふぁぼられたツイートをageる」の機能とも相まって、タイムライン上でふぁぼ爆撃合戦が始まると各ツイートが「シュパパパパパパ」と凄まじい勢いでウインドウ内を上下に飛び交う、という描画を目の当たりにすることになりました。

Redmine "miracle painter" チケット

実際の miracle painter の実装作業の様子が垣間見えるのが以下の mikutter Redmineのチケットです。

dev.mikutter.hachune.net

チケットの登録は 2011年5月8日のGW明け。おそらく、構想自体はGW中に練られたのではないかと思います。

そして、上述のとおり miracle painterが実装された mikutter 0.0.3.5 がリリースされたのは 2011年5月23日。

関連する子チケットから重そうなものだけをピックアップすると以下の通り。

つまり、わずか 2週間あまりでこれだけの機能が一気に実装されたことになります。

テキストの選択」って何? と思われるかもしれませんが、チケットの説明にある通り「ドラッグドロップでテキストを選択できるようにする」ということです。

どういうことかいうと、単純なテキストの描画も「全部画像」なので、 miracle painter つまり mikutter独自の実装として
マウスがクリックされた位置に描画されているテキストを識別し、ドラッグされた範囲の文字列を反転させる表示についても cairoで描画している
ということになるかと思います。

これだけを見ても「ヤバい」という雰囲気は感じられるのではないかと思います。

miracle painterの呪縛

一気呵成の勢いで実装された miracle painterですが、実装から数年の時が経過し、徐々に「勢いとのトレードオフ」とも言える互換性問題に直面していくことになります。

"miracle painter 2"

miracle painter の課題が記述されている資料の一つは、以下の "miracle painter 2" の mikutter Redmineチケットです。

dev.mikutter.hachune.net

起票は miracle painter 実装から7年後(!)の 2018年 5月14日。課題としてあげられているのは以下の項目です。

  • MiraclePainterのスーパクラスがGtk::Objectになっているのをやめる
    • 「Gtk3にはそんなクラス無いので」という記述に今日の問題が垣間見えるかと
  • MiraclePainterをTreeViewから独立させる
  • MiraclePainterを単一のプラグインにする
  • 再描画の最適化

しかし、このチケットに対する具体的なコミット等は行われることはなく、ほぼ1年後の 2019年 6月9日に「却下」でクローズされることになります。

"MiraclePainterをGtk::Widgetベースの実装に書き直す"

次に miracle painterの課題について言及されているのは、前述の yuntan_t氏の miracle painter対応の提案に対する以下の toshi_aさんコメントです。

まず、プロジェクトとしてMiraclePainterを置き換えるモチベーションはありません(gtk3は、色んな所に書いている通り、やりたいと思っています)。
UI周りのAPIが複雑なところであり、ここを置き換えると大きく互換性が損なわれます。多分、このチケットを提案した理由は速度やコードの単純化などだと思いますが、それらを達成するにはいまのMiraclePainterの互換性を壊さないことには十分な成果は得られないはずです。


とはいえ、互換性を無視して今書き直せば、数倍程度の高速化、半分程度のコード量は簡単に達成できると思います。互換性を気にして長い時間を掛け、大した効果を産めないよりは、forkなどの手段を取ってもらって、そういったしがらみを無視してMiraclePainterの書き換えはやってもらったほうが良いのではないか、という結論に至る可能性が高いと思っています。

提案 #1453: gtk3: MiraclePainterをGtk::Widgetベースの実装に書き直す - mikutter - やること

以前にも言及していますが、mikutterの大きな特徴は「プラグインによる拡張」が可能であるというところです。mikutter本体に付属するプラグインはいうに及ばず、各ユーザーが自由に実装したプラグインも、mikutter内部の miracle painterの構造に暗黙的に依存したものが多数存在します。

miracle painterの改善のために構造に手を入れようとしても、既存のプラグインに対する互換性維持という足枷により思い切った変更ができないという、世間の他のアプリでもよく見かける問題かと思います。たとえば、かの有名な動画編集ソフトである AviUtlでも同様のコメントが挙げられていました。

miracle painter関連コミット

前述のような困難がある中、 yuntan_t氏は持ち前の若さ(?)でこつこつと miracle painter の GTK3対応を進めていってくれました。

前述の通り「MiraclePainterを表示させることが出来ました」という報告が 2019年10月。

その後、最終的な方針転換が行われたのが 2020年 6月の以下のチケットコメント?

実装方針を変更しました.

  • MiraclePainterGtk::ListBoxRowにする.
  • MessageMixinincludeしたDiva::Modelを描画したいときは,Gtk::ListBoxを使う.
  • タイムラインに表示されるWidgetと,モーダルウィンドウの中に表示されるWidgetは同じウィジェット
機能 #1399: MiraclePainterをmoduleとして再実装する - mikutter - やること

細かい内容については、「わかる人にはわかる」ということで代表的なコミットのログと差分を見ていただくのがよいかと思います。

上記の miracle paiter対応以外にも、GTK2→GTK3移行において誰もが直面するであろう非互換対応として、「設定」「抽出タブ」「アクティビティ」「mastodon」「アカウント切り替え」「subparts (ふぁぼられ、引用等の表示)」等々のmikutter本体付属の各種プラグインに対する修正も多数コミットされています。

ruby-gnome "virtual function" 対応

miracle painter の GTK3対応で一番の大きな課題、そして、今年9月以降の本格的な mikutter GTK3対応活動にもつながったトピックとして、 ruby-gnome の "virtual function対応" というものがあります。

「virtual function とは何か」ということを書こうとするといくら紙面があっても足りない(というか私自身もちゃんと理解していない)のですが、yuntan_t氏が 2020年 5月5日に起票した以下の ruby-gnome に対する pull request を引用するのが早いかと思います。

Rubyで定義したサブクラスから,GObjectのvirtual functionをoverrideする機能です.

機能追加の目的は,GTK3のカスタムウィジット(Gtk::Widgetのサブクラス)のheight-for-width対応で, GtkWidget#get_request_mode等のvirtual methodをoverrideするためです.詳細はCustom WidgetsCustom Containersにあります.

gi: add support for implementing virtual functions in Ruby by yuntan · Pull Request #1386 · ruby-gnome/ruby-gnome · GitHub

上記で引用されている Custom Widgets と Custom Containers の説明をそれぞれ引用すると以下:

Custom Widgets

By deriving directly from Gtk::Widget you can do all the drawing for your widget directly, instead of just arranging child widgets. For instance, a Gtk::Label draws the text of the label, but does not do this by using other widgets.
When deriving from Gtk::Widget, you should override the following virtual methods.

Custom Containers

When deriving a custom container widget directly from Gtk::Widget, you should override the following virtual methods:

要は、「アプリが直接ウィジェットをいじるときに必要となる、メソッドのオーバーライドのための手法」という感じ。

この「アプリが直接ウィジェットをいじる」というのは、前述の通りまさに「miracle painterがやっていること」にほかなりません。実際に GTK3対応版 mikutter で使用されている virtual function は plugin/gtk3/widget/miraclepainter.rb 内の以下の3つのメソッドのようです。

# override virtual function Gtk::Widget.get_request_mode
def virtual_do_get_request_mode

# override virtual function Gtk::Widget.get_preferred_width
def virtual_do_get_preferred_width

# override virtual function Gtk::Widget.get_preferred_height_for_width
def virtual_do_get_preferred_height_for_width(width)

この pull request に対し、 ruby-gnome のメンテナである kou氏からの返信は以下:

ありがとうございます!
これをベースにフックのかけ方とかAPIとか整理して仕上げますね。

ただ、私が最近立て込んでいて、数ヶ月単位で先になってしまうかもしれません。ごめんなさい。
このブランチにpushしていくのでこのまま残しておいてもらえますか?

gi: add support for implementing virtual functions in Ruby by yuntan · Pull Request #1386 · ruby-gnome/ruby-gnome · GitHub

ただ、上述の通りメンテナの kou氏が多忙であったこと、さらに、 pull request 自体にもコンセプトレベルの実装が残っている状態であったことから、 ruby-gnome 本体への virtual function 取り込みにはかなりの時間を要することになりました。

この状況の中、 yuntan_t氏は mikutter の gtk3 プラグインGemfile に以下のような黒魔術を記述して開発を進めていくことになります。

dev.mikutter.hachune.net

実際の Gemfile の記述は以下。最近になってこの記述に気づいたときは「こんなことできるのか!(していたのか!)」と、ちょっと驚きました。

source 'https://rubygems.org'

git 'https://github.com/yuntan/ruby-gnome', branch: 'hook-up-vfunc' do
  gem 'gtk3'
end

virtual function pull request マージと gtk2 gem 削除

前述の virtual function pull request と ruby-gnome リリースの経過を時系列で書くと以下:

  • 2020/5/2
    ruby-gnome 3.4.2 リリース
  • 2020/5/5
    virtual function pull request起票
  • 同日
    kouさんより:
    「ただ、私が最近立て込んでいて、数ヶ月単位で先になってしまうかもしれません。ごめんなさい。」 
    「このブランチにpushしていくのでこのまま残しておいてもらえますか?」
  • 2020/5/11
    ruby-gnome 3.4.3 リリースWindows版の小修正のみ。
  • 2020/7/18
    kouさんより
    「(本当に数ヶ月経ってしまっているんですが、今月来月中にはマージできるようにしたいなぁという気持ちがあります。)」
  • 2020/8/30
    kouさんの virtual function ブランチへのコミット
  • 2021/5/12
    ようやくマージ。kouさんより
    「1年経ってしまいましたがようやく作業しました。。。」
    「既存のコールバック呼び出し処理を使うようにしたので大体の引数で動きます。」
  • 2021/7/7
    ruby-gnome 3.4.5 リリース
    "This is a release that supports implementing virtual functions in Ruby."

pull request起票から苦節1年、 mikutter GTK3対応のための virtual function サポートがようやく ruby-gnome 本体に取り込まれました!

……が、上記 ruby-gnome 3.4.5 リリースの前、 2021/4/22 にリリースされた ruby-gnome 3.4.4 リリースノート の中で 、mikutter自身の未来を左右する大きな事件(?)が起きていました。

Ruby/GTK2

  • Removed.

mikutter GTK3対応のためには、 ruby-gnome側での virtual function対応は必須。

一方、2021年5月当時の mikutter GTK3対応はおそらく道半ばの状態。GTK2版と比べると実用とするにはまだまだブラッシュアップが必要な段階だったと思われます。

ruby-gnomeからしてみれば、世間動向として GTK2が obsoleteとなるのは間違いない中で、 「virtual function対応という大きな変更を GTK2に入れる工数を投入する価値はない」という判断がなされたのではないかと推測しています。

「mikutter GTK3対応のために GTK2版 mikutterの将来が閉ざされる」という難しい状況の中、私が担当している pkgsrc の mikutter (のための ruby gtk2 パッケージ)のほか、積極的(?)な mikutterサポートで有名な debian apt の mikutter (のための ruby-gtk2 パッケージ)の各管理者も大きな決断を迫られることになります。

次回のエントリでは、この debian ruby-gtk2 パッケージのツイートを発端とした「mikutter GTK3対応の最終ステージ」について書いてみたいと思います。なんとか 12/25 の mikutter 5.0 リリースに間に合わせたい

mikutter GTK3対応(2) 〜サーベイ中〜

日経mikutter

前回の mikutter GTK3対応(1) のエントリとはあまり関係なく、日経Linux 2022年 1月号の付録冊子「あなただけの定番に!Ubuntuフリーソフト100+」において、 mikutter が「ファンの多い Mastodonクライアント」として紹介されました。

info.nikkeibp.co.jp

日経Linuxといえば、2013年12月の 2014年1月号でも「人気の Twitterクライアント」として取り上げていただいたことがあります。

togetter.com

日経Linuxの中の人に mikutterファンの方がいるからというわけではないのでしょうが、これまでにも4回以上 mikutterを取り上げていただいています。なお過去には Software Design誌でも mikutterが紹介されたことがあります

偶然とはいえ、 GTK3対応で大きく変わろうとしているこのタイミングで mikutterを紹介していただけたので、紹介誌は買って応援、 mikutter本体はブログ記事の続きでサポート、という具合いに相乗効果を出していければと思います。(いわゆるダイレクトマーケティング

Kindle版・Kobo版もあります

www.amazon.co.jpbooks.rakuten.co.jp

UbuntuTwitterクライアントmikutterを使う」

日経Linuxにおいて ubuntu用ソフトウェアとして mikutterが紹介されたこととは直接関係があるかはわかりませんが、つい先日の 12月10日に moerrariさんによる「UbuntuでTwitterクライアントmikutterを使う」という記事が投稿されています。

moerrari.hatenablog.com

前回の mikutter GTK3対応(1) のエントリ についても「貴重なmikutterの最新情報」として言及いただいている上に、一番の難関である Twitter CK/CS取得の詳細、mikutterを使ってみた上での問題点の指摘等々もいただいていて、「新たに mikutterを使う」という観点では非常に有用な記述になっていると思います。

さっそくですが、「リツイート(シェア)の背景色の設定が重複している問題」についてはチケット入れました。また、前回のエントリで適当な言及で済ませてしまった Twitterプラグインの GTK2版対応についてもちゃんと対応したいと思います。とりあえず "mikutter4" のブランチを切ってスクリプトで git clone 元を切り替える方向で調整中

「タイムラインの一番下で過去を遡ってツイートを取得する」という「使い方の提案」については、実はその内容に対応するチケットが長期間放置状態になってしまっているので、こちらもぼちぼち取り上げてみたいと思います。

dev.mikutter.hachune.net

mikutter GTK3対応の歴史 〜プロローグ〜

「日経Linuxubuntuも mikutterの GTK3対応とは関係ないじゃん!」と思われた方もいるかもしれませんが、今回の一連の記事を書くきっかけとなった最近の mikutter GTK3対応のアクティビティは、 ubuntuの mikutterパッケージの元となっている debianにおける mikutter対応とちょっとだけ関係していたりします。

そういった小ネタを含め、 mikutterの GTK3対応について1ユーザーとして横から観測できている範囲で本題である mikutter の GTK3対応の歴史と道のりをつらつら書いてみようと思います。

……と思って mikutter GTK3対応の始まりである Redmineチケット#887と関連コミットの調査サーベイをしていたのですが、サーベイ中の12月12日午後に追い越される形で toshi_a さんから「mikutter 5.0の新機能」のエントリが発表されてしまいました。

mikutter.hatenablog.com

このままこのエントリを書く意味あるのかというところもありますが、 toshi_aさんが以下のようにさらっと 3行で書かれているGTK3対応の各ポイントについて、もう少し突っ込んだ内容まで書ければいいかなと思っています。

今回のメインですが、新しくできるようになったことは特にありません。未実装項目や既知のバグが多数あります。

更に、サードパーティープラグインの互換性も一部失われています。これは気合で直すしかなく、mikutterの歴史の中で最も非互換なアップデートです。

もはやGtk2はサポートされていないので、できるだけ早く対応する必要がありました。

1つ目の「未実装項目や既知のバグが多数あります」については、背景となる GTK2からGTK3への移行の苦労を書き出すといくらエントリを書いても足りないというのが正直なところです。

一例として、 mikutterのGTK3対応関連のコミットを一通り確認したところ、今年9月以降の個別チケットがあるものを除いても 150を超えるコミットがありました。今回、サーベイのついでで該当するコミットをすべてRedmineチケットに関連付けして一覧として確認できるようにしたので、興味のある方はざっと眺めてみてください。

dev.mikutter.hachune.net

これらコミットのログと差分を見るだけでも GTK3対応の工数を察せられるのではないかと思います。

2つ目のプラグインのGTK2対応については、私自身も Twitter関連プラグインの修正でかなりの試行錯誤を強いられました。

次回以降は、このあたりの歴史や実際の修正作業について背景の解説(多分に推測を含む)を含めて書いてみたいと思います。

mikutter GTK3対応(1) 〜とりあえず動かしてみる〜

上記のツイートのように GTK3版 mikutter はそこそこ動くようになっているものの、現状では「mikutter gtk3」でGoogle検索してもほとんど情報がない状態です。

そこで、私自身で行ってきた mikutter GTK3対応作業を含め、何回かに分けて mikutter GTK3対応について書いてみたいと思います。

↓GTK3版 mikutter画面。タブ形状とTooltipの背景が黒という以外はわからないくらいに同じように使えます

f:id:tsutsuii:20211205141105p:plain

とりあえず動かしてみる

GTK3版の mikutter は今のところソースコード tar ball 等は用意されておらず、開発版のツリーを取得して動かす必要があります。

また、前述のツイートの通りで、 mikutter 動作に必要な Rubygtk3 gemについても ruby-gnome 3.5.0 が出るまではイレギュラーな暫定対応が必要です。

mikutterソースコードgit

mikutterのページdevelopのページの「開発版」の項にある通り、 mikutter のソースツリーは git://mikutter.hachune.net/mikutter.git の Gitでメンテされているため、そこから取得することになります。githubにも mikutter のコードが置かれていますが何かあったときのバックアップ扱いです

mikutterの Gitでは、リリース版(現状だと mikutter 4.1.x系)が master ブランチ、次の開発版(現状だと mikutter 4.2系)が develop ブランチ、 Redmineの各チケット案件別の対応ツリーが topic/[チケット番号]-[案件名] ブランチという管理構成を採っています。

本来であればGTK3版についても「次の開発版」という位置づけになりそうなものですが、 mikutter GTK3対応の歴史的経緯(?)から、GTK3版は Redmineチケット887 に対応する topic/887-gtk3 ブランチで管理されています。
mikutter の gtk3対応の歴史その他については次回以降のエントリで書く予定

GTK3ブランチ取得

mikutter などというクライアント、それも GTK3版を動かそうという方で git が入っていない人などいないという気はしますが、まずは各システムのパッケージシステムその他から git をインストールしてください。

NetBSDであれば

su
pkg_add -u https://cdn.netbsd.org/pub/pkgsrc/packages/NetBSD/`uname -p`/`uname -r`/All/git-base

ubuntu等であれば

sudo apt install git

等でいけると思います。

mikutter GTK3ツリーの取得は以下でできると思います。

git clone git://mikutter.hachune.net/mikutter.git ~/mikutter
cd ~/mikutter git switch topic/887-gtk3

※2021/12/14追記

2021/12/12 に無事に(?) topic/887-gtk3 ブランチが develop ブランチにマージされたので、今は git clone 後に

git switch develop

としてください。

追記ここまで

ruby-gnome開発版のインストール

※2021/12/11追記

このブログエントリを見た(?) shibafu528さんにより、 mikutter GTK3版に対して 提案 #1551: gtk3: ruby-gnome 3.4.9でも動くようにする - mikutter - やること黒魔術(?)パッチが投稿され、ほどなく toshi_a さんによりマージされました。

dev.mikutter.hachune.net

このため、2021/12/10以降の mikutter topic/887-gtk3 ブランチであれば、 ruby-gnome 3.4.9 リリース版でも動作するようになっています。

追記ここまで

冒頭のツイートにあるとおり、 mikutter GTK3版を動作させるにはリリース済みの ruby-gnome 3.4.9 ではダメで、最低限以下の2つの修正および変更を取り込んだものが必要です。

  • commit:554fe3e 親の親(およびそれ以上)のクラスの virtual function の修正
  • commit:73f123c pango gemのモジュール名の変更

特に後者の変更が別のRactor関連の変更の後に行われているため単独で cherry-pick することができず、機械的作業で対応しようとするとかなりの量のコミットを持ってくるハメになります。

とりあえず私の手元で確認用に fork したブランチではこれだけのgithub差分がありました。こんなにあるなら最新版を持ってきても変わらないという話もありますが、後述の pkgsrc 差分を作るのに不要なものは除きたかったので

とりあえず試す手順としては以下のいずれかになると思います。

  • 必要な差分を当てた状態のオレオレパッケージを作成する
  • インストール済みの ruby-gnomeの各gem (glib2 gobject-introspection gio2 pango gdk3 gtk3) の *.rb ファイルを必要な差分の当たった更新版のファイルと差し替える
  • mikutter の GTK3ブランチ付属の Gemfile を使って bundler により git最新版ベースの gem を作ってインストールする

環境にもよりますが、どれもある程度強引な方法なので、元に戻す方法の見当がつく中級者以上向けの内容になると思います。

なお、以下の記述では、すでに現状の mikutter 4.1.7 が動いている環境、つまり GTK3関連以外の必要なパッケージがインストール済みである前提で、それらの説明は省略しています。

(1) ローカル差分を当てたpkgsrcを作成する

とりあえず自分自身の NetBSD環境でのテスト用に pkgsrc-2021Q3 に対する差分ファイル一式を用意してあります。また、NetBSD/i386 9.2 および NetBSD/amd64 9.2 についてはバイナリパッケージも作成してあります。

バイナリパッケージを使う場合は以下のサーバーにこちらで作成したバイナリが置いてあります。

NetBSD/i386 9.2:

pkg_add -u http://teokurebsd.org/netbsd/packages/i386/9.2_2021Q3/All/ruby27-gtk3-3.4.9nb1.tgz

NetBSD/amd64 9.2:

pkg_add -u http://teokurebsd.org/netbsd/packages/x86_64/9.2_2021Q3/All/ruby27-gtk3-3.4.9nb1.tgz

自分自身で pkgsrc-2021Q3 からバイナリパッケージを作成する場合は、 pkgsrc-2021Q3 のツリーに対して以下の tar ball のファイルを上書きして ruby-gtk3 および依存パッケージをビルドしてください。

http://teokurebsd.org/netbsd/tmp/pkgsrc-2021Q3_ruby-gnome-3.4.9nb1_for_mikutter_gtk3.tar.gz

含まれているファイルは以下:

devel/ruby-glib2/Makefile
devel/ruby-glib2/distinfo
devel/ruby-glib2/buildlink3.mk
devel/ruby-glib2/patches/patch-ext_glib2_rbglib__error.c
devel/ruby-glib2/patches/patch-ext_glib2_rbgobj__boxed.c
devel/ruby-glib2/patches/patch-ext_glib2_rbgobj__closure.c
devel/ruby-glib2/patches/patch-ext_glib2_rbgobj__enums.c
devel/ruby-glib2/patches/patch-ext_glib2_rbgobj__flags.c
devel/ruby-glib2/patches/patch-ext_glib2_rbgobj__object.c
devel/ruby-glib2/patches/patch-ext_glib2_rbgobj__param.c
devel/ruby-glib2/patches/patch-ext_glib2_rbgobj__signal.c
devel/ruby-glib2/patches/patch-ext_glib2_rbgobj__type.c
devel/ruby-glib2/patches/patch-ext_glib2_rbgprivate.h
devel/ruby-gio2/Makefile
devel/ruby-gio2/distinfo
devel/ruby-gio2/buildlink3.mk
devel/ruby-gio2/patches/patch-lib_gio2_loader.rb
devel/ruby-gobject-introspection/Makefile
devel/ruby-gobject-introspection/distinfo
devel/ruby-gobject-introspection/buildlink3.mk
devel/ruby-gobject-introspection/patches/patch-ext_gobject-introspection_rb-gi-arguments-out.c
devel/ruby-gobject-introspection/patches/patch-ext_gobject-introspection_rb-gobject-introspection.c
devel/ruby-gobject-introspection/patches/patch-lib_gobject-introspection_loader.rb
devel/ruby-gobject-introspection/patches/patch-lib_gobject-introspection_type-tag.rb
devel/ruby-gobject-introspection/patches/patch-test_test-loader.rb
devel/ruby-pango/Makefile
devel/ruby-pango/distinfo
devel/ruby-pango/buildlink3.mk
devel/ruby-pango/patches/patch-lib_pango.rb
devel/ruby-pango/patches/patch-lib_pango_cairo-loader.rb
devel/ruby-pango/patches/patch-lib_pango_fc-loader.rb
devel/ruby-pango/patches/patch-lib_pango_ft2-loader.rb
devel/ruby-pango/patches/patch-lib_pango_loader.rb
devel/ruby-pango/patches/patch-lib_pango_ot-loader.rb
graphics/ruby-gdk3/Makefile
graphics/ruby-gdk3/distinfo
graphics/ruby-gdk3/buildlink3.mk
graphics/ruby-gdk3/patches/patch-lib_gdk3.rb
graphics/ruby-gdk3/patches/patch-lib_gdk3_loader.rb
graphics/ruby-gdk3/patches/patch-lib_gdk3_x11-loader.rb
x11/ruby-gtk3/Makefile
x11/ruby-gtk3/distinfo
x11/ruby-gtk3/buildlink3.mk
x11/ruby-gtk3/patches/patch-lib_gtk3_loader.rb
graphics/ruby-clutter/Makefile
graphics/ruby-clutter/distinfo
graphics/ruby-clutter/patches/patch-lib_clutter.rb
multimedia/ruby-gstreamer/Makefile
multimedia/ruby-gstreamer/distinfo
multimedia/ruby-gstreamer/patches/patch-lib_gst.rb
multimedia/ruby-gstreamer/patches/patch-lib_gst_base-loader.rb
multimedia/ruby-gstreamer/patches/patch-lib_gst_controller-loader.rb
multimedia/ruby-gstreamer/patches/patch-lib_gst_loader.rb
multimedia/ruby-gstreamer/patches/patch-test_run-test.rb

なお ruby-clutterruby-gstreamer については mikutter を使うだけならビルドは不要です。

(2) インストール済みの各gemの *.rb ファイルを差し替える

ruby-gnome で必要になる差分の中には C言語で記述された extension のソースもありますが、とりあえず強引に *.rb ファイルだけを差し替えてもとりあえず mikutter は起動するようです。よって、すでにパッケージシステムその他でインストールした ruby-gnome 3.4.9 の各gemのインストール済みの *.rb ファイルを必要なファイルに置き換えるという方法も一応アリです。

pkgsrc-2021Q3 の場合は /usr/pkg/lib/ruby/gems/2.7.0/gems 以下に、 ubuntu 20.04 の apt の場合は /var/lib/gems/2.7.0/gems 以下に gem関連のファイルが置かれるようなので、その中の必要な *.rb ファイルをオレオレruby-gnomeツリーから持ってきたファイルに置き換える、という操作をすることになります。

具体的には、以下のファイルを置き換えればいけると思います。当然ながら無保証です

gdk3-3.4.9/lib/gdk3.rb
gdk3-3.4.9/lib/gdk3/loader.rb
gdk3-3.4.9/lib/gdk3/x11-loader.rb
gio2-3.4.9/lib/gio2/loader.rb
gobject-introspection-3.4.9/lib/gobject-introspection/loader.rb
gobject-introspection-3.4.9/lib/gobject-introspection/type-tag.rb
gtk3-3.4.9/lib/gtk3/loader.rb
pango-3.4.9/lib/pango.rb
pango-3.4.9/lib/pango/cairo-loader.rb
pango-3.4.9/lib/pango/fc-loader.rb
pango-3.4.9/lib/pango/ft2-loader.rb
pango-3.4.9/lib/pango/loader.rb
pango-3.4.9/lib/pango/ot-loader.rb
(3) mikutterの Gemfile による bundler を使って gemをインストールする

mikutter は rubybundler gem と Gemfile の記述によるインストールにも対応しています。bundlerの仕組みについては適当にググってもらうとして、GTK3版のブランチの mikutter の plugin/gtk3/Gemfile には以下のような通常の Gemfile の記述とは異なる黒魔術(?)が入っています。

source 'https://rubygems.org'

git 'https://github.com/ruby-gnome/ruby-gnome', ref: '554fe3ebba5da5f4b1f61ac7b5e6121be1b32b51' do
  gem 'gtk3'
end

このため、通常の bundler 操作でも GTK3版 mikutterに対応した ruby-gnome の各gemがインストールされます。

私自身は pkgsrc 環境がメインのため bundler の設定をよくわかっていませんが、 ubuntu 20.04 の場合は以下のように bundler gem を設定すれば mikutter を起動できるようになるようです。

sudo apt install ruby-dev ruby-bundler
cd [mikutter topic/887-gtk3 ブランチを展開したディレクトリ]
sudo bundle install

手元の virtualbox 環境での実行結果は以下のような感じです。

tsutsui@ubuntu-vbox:~/mikutter$ sudo bundle install
Don't run Bundler as root. Bundler can ask for sudo if it is needed, and
installing your bundle as root will break this application for all non-root
users on this machine.
Using rake 13.0.6
Using public_suffix 4.0.6
Using addressable 2.7.0
Using native-package-installer 1.1.1
Using pkg-config 1.4.6
Using glib2 3.5.0 from https://github.com/ruby-gnome/ruby-gnome (at 554fe3e@554fe3e)
/usr/lib/ruby/2.7.0/rubygems/ext/builder.rb:165: warning: conflicting chdir during another chdir block
/usr/lib/ruby/2.7.0/rubygems/ext/builder.rb:173: warning: conflicting chdir during another chdir block
Using atk 3.5.0 from https://github.com/ruby-gnome/ruby-gnome (at 554fe3e@554fe3e)
/usr/lib/ruby/2.7.0/rubygems/ext/builder.rb:165: warning: conflicting chdir during another chdir block
/usr/lib/ruby/2.7.0/rubygems/ext/builder.rb:173: warning: conflicting chdir during another chdir block
Using bundler 2.1.4
Using matrix 0.4.2
Using red-colors 0.3.0
Using cairo 1.17.5
Using cairo-gobject 3.5.0 from https://github.com/ruby-gnome/ruby-gnome (at 554fe3e@554fe3e)
/usr/lib/ruby/2.7.0/rubygems/ext/builder.rb:165: warning: conflicting chdir during another chdir block
/usr/lib/ruby/2.7.0/rubygems/ext/builder.rb:173: warning: conflicting chdir during another chdir block
Using rexml 3.2.5
Using crack 0.4.5
Using delayer 1.2.1
Using delayer-deferred 2.2.0
Using diva 1.1.1
Using gobject-introspection 3.5.0 from https://github.com/ruby-gnome/ruby-gnome (at 554fe3e@554fe3e)
/usr/lib/ruby/2.7.0/rubygems/ext/builder.rb:165: warning: conflicting chdir during another chdir block
/usr/lib/ruby/2.7.0/rubygems/ext/builder.rb:173: warning: conflicting chdir during another chdir block
Using gio2 3.5.0 from https://github.com/ruby-gnome/ruby-gnome (at 554fe3e@554fe3e)
/usr/lib/ruby/2.7.0/rubygems/ext/builder.rb:165: warning: conflicting chdir during another chdir block
/usr/lib/ruby/2.7.0/rubygems/ext/builder.rb:173: warning: conflicting chdir during another chdir block
Using gdk_pixbuf2 3.5.0 from https://github.com/ruby-gnome/ruby-gnome (at 554fe3e@554fe3e)
/usr/lib/ruby/2.7.0/rubygems/ext/builder.rb:165: warning: conflicting chdir during another chdir block
/usr/lib/ruby/2.7.0/rubygems/ext/builder.rb:173: warning: conflicting chdir during another chdir block
Using pango 3.5.0 from https://github.com/ruby-gnome/ruby-gnome (at 554fe3e@554fe3e)
/usr/lib/ruby/2.7.0/rubygems/ext/builder.rb:165: warning: conflicting chdir during another chdir block
/usr/lib/ruby/2.7.0/rubygems/ext/builder.rb:173: warning: conflicting chdir during another chdir block
Using gdk3 3.5.0 from https://github.com/ruby-gnome/ruby-gnome (at 554fe3e@554fe3e)
/usr/lib/ruby/2.7.0/rubygems/ext/builder.rb:165: warning: conflicting chdir during another chdir block
/usr/lib/ruby/2.7.0/rubygems/ext/builder.rb:173: warning: conflicting chdir during another chdir block
Using locale 2.1.3
Using text 1.3.1
Using gettext 3.3.9
Using gtk3 3.5.0 from https://github.com/ruby-gnome/ruby-gnome (at 554fe3e@554fe3e)
/usr/lib/ruby/2.7.0/rubygems/ext/builder.rb:165: warning: conflicting chdir during another chdir block
/usr/lib/ruby/2.7.0/rubygems/ext/builder.rb:173: warning: conflicting chdir during another chdir block
Using hashdiff 1.0.1
Using httpclient 2.8.3
Using instance_storage 1.0.0
Using memoist 0.16.2
Using mini_portile2 2.6.1
Using mocha 1.13.0
Using moneta 1.4.2
Using racc 1.6.0
Using nokogiri 1.12.5 (x86_64-linux)
Using oauth 0.5.8
Using pluggaloid 1.7.0
Using power_assert 2.0.1
Using ruby-prof 1.4.3
Using test-unit 3.5.1
Using typed-array 0.1.2
Using webmock 3.13.0
Bundle complete! 18 Gemfile dependencies, 42 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.

bundlesudo 付きで起動すると上記の通りいろいろ警告されるのですが、 sudo なしで実行すると glib2 gem の extension のビルドでパーミッション関連で怒られたような……。細かいところは ubuntu および bundler に詳しい方にお任せしたい

ただ、 apt のようなパッケージシステムがインストールファイルを管理しているであろう環境で bundler により gem を置き換えた場合にどうなるのかというのはよくわかっていません。このあたりは自己責任でお願いします。

mikutterの起動

ここまででだいぶ力尽きている感じですが、デフォルトでサポートされている Mastodon のクライアントとして使うだけならこれで mikutter の起動は可能です。

pkgsrc のように bundler を使わずに環境設定をした場合は以下のように環境変数 DISABLE_BUNDLER_SETUP1 に設定して mikutter ソースツリーの mikutter.rb を引数に ruby を起動します。人柱テストとしては --debug オプションも指定したほうが良いでしょう。

$ DISABLE_BUNDLER_SETUP=1 ruby27 mikutter.rb --debug

csh系 shellなら以下

% env DISABLE_BUNDLER_SETUP=1 ruby27 mikutter.rb --debug

bundler で設定した場合はそのまま mikutter.rb を指定して起動すればOKです。

$ ruby mikutter.rb --debug

mastodonなんてやってないよ」という方も、 mikutterを使うのであればせっかくなので https://social.mikutter.hachune.net/ に登録して toshi_a さんの動向を観察するのもよいのではないでしょうか。

サードパーティープラグインのGTK3対応

toshi_aさんのプレゼンにもあるとおり、各ユーザーが作成したプラグインで各種の機能拡張ができることがmikutterの大きな特徴です。これらの各種プラグインについても、 GTKに依存しているものは GTK3対応の修正が必要になる場合があります。

変更内容の詳細については mikutter本体のGTK3対応と合わせて別エントリで書く予定なので、ここでは対応状況の説明のみを記載します。

Twitterプラグイン

github.commikutter 4.0からはオプションになった、 mikutterで Twitterを使うためのプラグインです。

Twitterを使うためには mikutter に対して機能別の複数のプラグインのインストールが必要ですが、上記 twitter_bootstrap のページに記載のあるワンライナーもしくは twitter-bootstrap.shスクリプトを実行することで必要なプラグイン一式をインストール可能です。

なお、実際に Twitterを利用するには別途 twitter_api_keys プラグインによるコンシューマーキーの登録が必要ですが、その設定についてはGTK3版固有の話ではないので説明は割愛します。

各種TwitterプラグインのGTK3対応については、私の方で作業していた変更本家mikutter側のgithubにそれぞれマージしてもらっているので、上記のワンライナーもしくは twitter-bootstrap.shスクリプトを実行すればそのまま GTK3版として使用可能です。逆に、現状ではタグやバージョンを指定していないので従来のGTK2版でこのまま設定すると動かなくなっています。そのうちなんとかするかも

各種Twitterプラグインのうち、実際にGTK3対応の変更が入っているのは以下の6つです。

sub-parts-clientプラグイン

github.com"via client名" の形式の投稿クライアント名を追加するプラグインです。Twitterだけでなく Mastodonの投稿にも対応しています。

GTK3 対応と言っても、プラグイン中の .mikutter.yml plugin: 行に続く依存プラグイン記述の gtkgtk3 に書き換えるだけでOKです。

mikutter-uwm-hommageプラグイン

github.com謎のプラグイン名ですが、Twitterでの画像投稿を可能にするプラグインです。 hommage というのは「オマージュ」ですね

こちらは私で fork した GTK3対応版ブランチでそれなりに動いているのでそちらを git clone してください。

$ mkdir -p ~/.mikutter/plugin; git clone https://github.com/tsutsui/mikutter-uwm-hommage.git ~/.mikutter/plugin/mikutter-uwm-hommage

mikutter本体側で GTK3版が正式リリースされたら正式に pull request を出そうと思っています。

subparts_imageプラグイン

github.com説明通り、メッセージに添付された画像を mikutter タイムライン中に表示するプラグインです。 Twitter だけでなく Mastodon トゥートの添付画像、各種画像サービスの画像のプレビューにも対応しています。

こちらは shibafu528 さんによる GTK3版の fork が作成されていますので、それをそのまま使えばOKです。※誤記訂正 2021/12/6 thanks shibafu528さん

$ mkdir -p ~/.mikutter/plugin; git clone https://github.com/shibafu528/mikutter-subparts-image.git ~/.mikutter/plugin/mikutter-subparts-image
その他のプラグイン

その他のプラグインで GTK3対応についてはとりあえず以下のようになると思います

  • .mikutter.yml を見て gtk プラグインに依存しているかを確認し、依存していたらとりあえず gtk3 に書き換えてみる
    プラグインによっては gtk に依存していても依存関係が明示的に記載されていないものがあるので注意
  • mikutter を --debug オプション付きで起動してプラグインが意図通り動いているかを見る
  • 動いていないようであれば頑張って GTK3対応を調べる 詳細は次回以降のエントリで

mikutter GTK3版 バグレポート

mikutter Redmine の「バージョン」の 「gtk3」 「5.0」で検索すると gtk3関連のチケット一覧を確認可能です。※2021/12/12追記: toshi_aさんにより「gtk3」バージョンが正式に「5.0」の名称に変更されました

個人的には使い勝手に関連する以下が直るといいなと思っていますが、これらはそもそも GTK3版で使用する Widget が変更されたところなので、従来どおりの動作が可能なのかは不透明です。

mikutter GTK3版のリリースについて

例年であれば mikutter の誕生日である 12月25日に新バージョンのリリースが行われるのが常で、 GTK3版についても今年の 12月25日に mikutter 5.0 としてリリースされる構想はあるようなのですが、上述の toshi_a さんのトゥートにある通り ruby-gnome 3.5.0 のリリースがどうなるかにかかっている、という状況です。

ruby-gnome 最新版の正式リリースが行われない場合、このエントリで説明したようにインストールがかなり煩雑なので、現状では先行き不透明な状況です。

まあ、お互い趣味活動のオープンソース活動なので、時が来ればなるようになることを期待したいと思います。

NetBSD と Adobe Flash Player の思い出

このアゴアニキさんのツイートを見て、やっぱりクリエイターと呼ばれる方々には Flash そのものに思い入れがある方も多いのだろうなあ、と感慨深く思ってしまったのを機会に、 NetBSD と pkgsrc で 5年以上 adobe-flash-player のプラグインパッケージをメンテしてきた思い出(?)についてつらつら書いてみようと思います。

Adobe Flash Player について

記事の導入で困ったときには Wikipedia、ということで Adobe Flash のページから引用すると

Adobe Flash(アドビ・フラッシュ)は、かつてアドビが開発していた動画ゲームなどを扱うための規格、およびそれを作成・動作させるアプリケーション群。

とサラッと書いてあります。

かつては、先頭に貼った「ダブルラリアット」の動画のように Flash ならではの機能を使って「止めても動く」といったギミックを組み込んだ作品やゲームも多数ありました。ただ、 HTML5 による動画再生が当たり前となった今とは違い、 2010年頃の時代を振り返るとYouTube を始めとする動画サイトでの動画視聴をするだけでも Flash が必須」という状況だったかと思います。

NetBSD における Adobe Flash プラグインバイナリ

前身の Netscape から Firefoxオープンソース化されて NetBSD などというマイナーOS上でもネイティブアプリとしてビルドできるようになったのはいつなのかをログで漁ってみると、 2004年 3月に firefox-0.8 がインポートされたのが最初のようです。その後も順調にアップデートされていたようですが、私自身が pkgsrc の firefox を使うようになったのは 2009年頃、 firefox のバージョンでいえば 3.5.x の頃のようです。

過去のツイートを探すと、 2010年には NetBSD 上から YouTubeNetBSD動画の投稿もしていて pkgsrc の adobe-flash-plugin (当時) を使って動画再生もしていたようです。 Flash のバージョンは 9 もしくは 10 という時代。

2011年になると adobe-flash-plugin もバージョン 11 になってそれを使っていたようです。

GD-ROMドライブの駆動音動画はこちら

Firefox は前述の通りオープンソース化されていましたが、 Adobe製の Flash player は知っての通りいわゆるプロプライエタリバイナリで、利用にあたっては Adobe が配布しているバイナリをダウンロードする必要があります。かつては Solaris のようなマイナー(?) OS用のバイナリが配布されていたこともありますが、 BSD系OS については NetBSD は当然(?)として FreeBSD用についてもバイナリは一切用意されていない状態で、存在するのは Windows用、 MacOS用、そして x86Linux用のバイナリのみという状態でした。

その状態でどうやって NetBSD上で flash player を動かしていたのか、というのがこの記事の本題の nspluginwrapper (リンクは archive.orgアーカイブ) というツールです。

Adobe Flash プラグインnspluginwrapper

Adobe から提供される Flash Player のバイナリは libflashpler.so というダイナミックリンクライブラリと同様の shared object 形式になっています。

% file libflashplayer.so
libflashplayer.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, BuildID[sha1]=(略), stripped

つまり、 libflashpler.soLinuxネイティブバイナリであり、これをプラグインとしてロードできるのは ABIが共通である LinuxFirefox のネイティブバイナリのみ、ということになります。

nspluginwrapper の原理

Linux用バイナリしか提供されていない Flash プラグインバイナリを Linux以外のブラウザでも動かすことはできないか、ということを目的として開発されたのが nspluginwrapper です。 nspluginwrapper は「多くのOSが Linuxバイナリエミュレーションレイヤーを持っており Linuxバイナリを実行可能である」というところに着目して、以下の図のような原理で Linux バイナリである libflashplayer.so を動作させます。

  1. Firefox本体バイナリ用のプラグインとして nspluginwrapper が提供する仲介用プラグイン (npwrapper.libflashplayer.so)を用意する
  2. npwrapper.libflashplayer.soFirefox から起動されると、 nspluginwrapper パッケージとして用意される Linuxバイナリの npviewer.bin バイナリを npviewer スクリプト経由で起動する
  3. npviewer.bin バイナリは Adobe から配布される libflashplayer.so バイナリを使用して Flash 再生を実行する
  4. npviewer.bin バイナリと Firefox プラグインとして動作している npwrapper.libflashplayer.so がプロセス間通信を使用して再生結果をブラウザに伝達する

ネイティブ実行と比較すると途中にプロセス間通信が入るため描画速度といった性能面では不利ですが、とりあえず当時の動画サイズであれば視聴に支障のない程度の速度は出ていました。

nspluginwrapper の開発経緯

nspluginwrapper のもともとの開発動機は BSD系OSで Flash Player を動かすことではなく、開発元のページにあるように「64ビットの x86_64 Linux用ブラウザで 32ビットの x86 Linux用の libflashplayer.so バイナリを使用する」ためでした。前述の図で「NetBSDバイナリ」の部分を「64ビット Linuxバイナリ」に、「Linuxバイナリ」の部分を「32ビット Linuxバイナリ」に置き換えて、64ビット Linuxが 32ビットバイナリを実行できることを含めればコンセプトはほぼ同じであることがわかるかと思います。

この 64ビット Linuxのために開発された nspluginwrapper を、 FreeBSDNetBSD といった他OSでのユーザーが応用して自分たちの OSでも Flash 再生を可能にするために利用した、というのが正確なところです。

余談ですが、BSD系OSのカーネルの持つ Linuxバイナリエミュレーションに限らず、何らかの手段で「Linux用の libflashplayer.so を実行する」ことさえできれば、どんなOSでも同様のしくみで Flash 再生が可能であるといえます。 nspluginwrapper のドキュメントには「QEMUのユーザーバイナリ実行機能を使って Flash を動かす方法」についても言及されていましたが、実際には実行速度の問題でかなりキツいのではないかと思います。

私と Adobe Flash Player

ここまでは単に「一ユーザーとして pkgsrc の firefox, nspluginwrapper, adobe-flash-plugin を使っていた」というだけでした。YouTube の視聴についても、当時は Flash Player 互換プロジェクトとして作成されていた gnash でもある程度動いていたこともあり、2012年のオープンソースカンファレンス北海道で発表した NetBSD用 Live Image でも gnash を組み込んでいたくらいで、 Flash Player についてはたまに使う程度でした。それが 2015年頃から一転して pkgsrc の adobe-flash-plugin および nspluginwrapper のメンテをするようになったのはなぜなのか、というところから書いてみようと思います。

ニコニコ動画Flash Player

Flash を多用するようになったきっかけといっても大したことではなく、単に 2014年の夏頃からニコニコ動画をよく見るようになった、というところからです。ちょうどこの頃に個人的な環境変化もあり精神的にキツい状況に置かれていたのですが、ニコ動にアップされている「UP主のエネルギーがこれでもか投入された動画」「人間の可能性を知らしめる動画」を見て精神の安寧を保つ、という時期が続いていました。その頃に元気をもらった動画のうち、今でも印象に残っているものを挙げると以下の 2つでしょうか。

まあ、特定の動画が問題だったというわけではなく、単に再生する回数が増えたせいで nspluginwrapper もしくは Flash プラグインバイナリが落ちることが多くなり、それが日に日に気になってどうにかしたいと思うようになった、というのが実際のところです。

nspluginwrapper のバージョンアップ問題

本家の nspluginwrapper のリリースは 2011年6月に出た 1.4.4 が最後で、 github のコミットも 2011年12月29日を最後に行われていません。

一方、 pkgsrc の nspluginwrapper は 2015年1月時点でも 1.2.2 という 2009年1月リリースのものが使われており 1.4.4 どころか 1.3.0 へのバージョンアップも行われていない状態でした。これは単にメンテナ不在で更新が止まっていたわけではなく、 1.3.0以降のバイナリがうまく動いていないということのようでした。

とりあえず適当に pkgsrc/www/nspluginwrapper の中身を 1.4.4 に更新してみたところ、確かに RPCまわりのエラーメッセージがいろいろ出て動かないようでした。が、そのメッセージでググってみると、実はすでに関連するバグレポが出ているということが判明しました。

Linux anonymous socket

上述の PR/47208 の Probrem Report に
a hack to work around the usage of anonymous sockets in the linux npviewer.bin binary
とあるとおり、 nspluginwrapper 1.3.0 以降では Linux バイナリの npviewer.bin 側が Linux 固有の anonymous socket というものを使うように変更されており、通信相手の NetBSDプラグインバイナリもそれに対応する必要がある、ということのようでした。この問題に対し、 PR/47208 のパッチでは以下のような対処をしていました。

  1. Linux の anonymous socket を処理するための専用の bind() 関数を実装する
  2. その bind() を実装したバイナリを libnoanonsocket.so として nspluginwrapper のパッケージに含める
  3. libc 内の bind(2) を差し替えるために、プラグイン用バイナリ起動の npviewer.sh のスクリプトで LD_PRELOAD=${NPW_VIEWER_DIR}/libnoanonsocket.so を指定する
具体的な説明差分はこちら

確かにこの対処で Flash プラグインは動くようにはなったのですが、以下のような課題がありコミットはしづらい状態でした。

結局、以下が正解かと思いつつ、ビルド用の Linux環境を用意するのが面倒で一時的に止まってしまいました。

OpenSUSE 環境整備

2周間ほど経過した後に再度トライ。

ビルド環境の整備というかビルドに必要なパッケージの設定に手間取ったように記憶していますが、YaSTその他のパッケージ設定については本題でないので省略。いろいろ調べた結果として、 nspluginwrapper のビルド時の configure のオプションで --enable-generic を指定すれば anonymous socket ではなく従来通り generic(?) な socket を使うものがビルドでき、こうして作ったバイナリは NetBSD 上でも動作することが確認できました。

bind(3) は間違いで bind(2) が正解ですね

x86_64 64ビット対応

ここまでいろいろ調べた結果として、前述したとおり nspluginwrapper の開発目的が「64ビット LinuxFirefox バイナリで 32ビットの libflashsupport.soプラグインバイナリを使用する」ためである、ということがなんとなくわかってきました。また、2015年時点ではすでに 64ビット Linux 用の libflashsupport.soプラグインバイナリもリリースされており、 nspluginwrapper の Linux における開発動機はほぼ失われてしまっているということもわかりました。

一方、 64ビット Linux用の libflashsupport.soプラグインバイナリが存在するということは、従来は COMPAT_LINUX32 の 32ビット Linuxバイナリエミュレーションを使用していた NetBSD/amd64 でも 64ビットの libflashsupport.so が使えるのでは? ということも考えられました。

いろいろ調べた結果、 64ビットの OpenSUSE 12.1 上で以下のようにビルドすれば 32ビットの i386版同様で amd64 でも 64ビットの libflashsupport.so を使って Flash 再生も動くことが確認できました。

この "--disable-biarch" のオプションが分かりづらかったところで、 64ビット環境だとデフォルトで「32ビットのプラグインバイナリを動かすためのバイナリ」も同時に作られてしまう、ということのようでした。 "--disable-biarch" を指定すれば 32ビット版同様で 64ビット版プラグインバイナリ実行に必要なバイナリのみが作成されます。

さらなる不安定問題

pkgsrc でバイナリファイルを直接使用している事例についても確認し、「64ビットバイナリでも動く」という明確なメリットがあれば自前でビルドした nspluginwrapper バイナリを使うのも許されるだろう、と思って github 作業ファイルベースでコミットの準備を進めました。しかし、 firefox 35.0 が出た頃に確認したところ 1.2.2 と比べて微妙に動作が不安定な部分があるのがわかってきました。

従来の nspluginwrapper 1.2.2 だとこの現象は起きないようだったのですが、それはそれで従来どおり「おもむろに落ちている」という現象が復活してしまいます。

解決へ

この微妙な現象の問題の原因がわからずコミットもできないままだったのですが、3か月ほど経過した後にたまたまソースを確認してふと試したところ、原因らしきものが判明しました。

ここまでの nspluginwrapper 1.4.4 対応についてまとめたのが以下のツイート。

コミットまで

詳細は省略しますが、 Flash 再生において pulseaudio ではなく oss を使用するための libflashsupport パッケージについても 64ビット版バイナリを作成してようやくコミットできる動作になりつつありました。しかし、この libflushsupport バイナリのバージョン番号をどう扱うか、および、自前ビルドの Linuxバイナリを使用することの是非の相談を pkgsrc-bugs のメーリングリストに投げたところ誰からも返事をもらえず、自分自身も忙しかったのもあってさらに時間がかかることになってしまいました。バイナリにサインしろとか言われたらやる気なくしそうだった

64ビット版バイナリでは pulseaudio を使う設定では動作せず libflashsupport バイナリを使った oss 設定でしか動作しないという問題が残ったままという懸念もあり、結局コミットしたのは pkgsrc-2015Q3 が出た後の 2015年10月も終わりに近づいた頃でした。事前の pkgsrc-bugs のメールでは賛成意見も反対意見も出なかったので、バイク小屋議論を避けるためと未来の自分のためにやたらくどく書いたコミットログがこちら

Log Message:
Update nspluginwrapper-1.4.4.

pkgsrc changes:
- use private Linux npviewer.bin binaries built by me (tsutsui@) on
openSUSE 12.1 on 32 bit (i386) and 64 bit (x86_64) on VirtualBox
- enable EMUL_PLATFORMS=linux-x86_64 using the native 64 bit Linux
npviewer.bin binary, which allows using 64 bit native adobe-flash-plugin
on NetBSD/amd64 hosts
- also explicitly set EMUL_REQD= suse>=12.1 (NetBSD 6.x can use it anyway)
- tweak some pkgsrc ${PREFIX}
- update HOMEPAGE
- take maintainership

Note:
- major Linux distributions provided nspluginwrapper binaries to use
the 32 bit plugin binaries without sources on their 64 bit systems,
so there is no 64 bit wrapper binary (npviewer.bin) to use native 64 bit
plugin binaries on other systems (like NetBSD) via binary emulation
- nowadays adobe provides 64 bit native adobe-flash-plugin11 binaries
and NPAPI plugins are being deprecated by vendors, so I guess there is
very few motivation to update nspluginwrapper project for Linux people
http://nspluginwrapper.org/why.html
- Linux binaries in distfiles are built with following changes to
make npviewer.bin works on non-Linux hosts:
- configure with the following options, to enable "generic" RPC calls
(The default Linux native binaries use their specific "anonymous socket")
- for i386:
% ./configure --enable-generic
- for x86_64:
% ./configure --target-cpu=x86_64 --disable-biarch --enable-generic --enable-viewer --enable-player
- disable USE_NPIDENTIFIER_CACHE in npviewer (as patch-src_npw-viewer.c),
which doesn't seems tested with the "generic" RPC interfaces

OK from abs@, and no particular objection to PR pkg/49705 and pkgsrc-users@.
Also thanks to Onno van der Linden for his first analysis about
newer nspluginwrapper APIs in PR pkg/47208.

このスピード感の無さが NetBSD の悪いところというか、ユーザーの需要関係なく開発者側が思う存分考えて納得できるところまでやるというのは良くも悪くも伝統かな、などと思ったりはします。結局、 Flash のサポートが終了する今日までこのときのバイナリが使われ続けることになりました。
実際に使っていた人がどれくらいいたのかは定かではありません

pkgsrc と Adobe Flash Player その後

ここまでで書いたように「プロプライエタリLinuxバイナリとネイティブバイナリを組み合わせて使う」という目的の実現のために多くの知恵と技術が詰まっていることに感銘を受けたこと、そして実際にそうして作られたソフトを日常生活でも毎日のように使って満足している、というところが 2020年になっても pkgsrc の adobe-flash-player の更新とメンテを続けてきた理由だったりします。

ここからは、 2015年以降 Flash Player の NetBSD対応で発生した技術的な問題と対処について思いつくまま書いていきたいと思います。

Adobe Flash Player 11.2 と NPAPI プラグインサポート

私が adobe-flash-player に積極的に関わるようになる前の 2012年 2月の Flash Player 11.2 のリリースの時点で、 LinuxFirefox 向けの NPAPI プラグインのサポートについて以下のようなアナウンスが出されていました。

つまり、 pkgsrc に nspluginwrapper 1.4.4 をコミットした 2015年10月時点では LinuxFirefox (つまり NetBSDFirefox) の Flash サポートは残り 1年ちょっと、という状況でした。

NPAPI版サポート終了に伴う Linux界隈の対応の一つとしては「サポートの続いている PPAPI版プラグインに NPAPIの wrapper を被せる」という freshplayer というプロジェクトが存在しました (一応 2020年にもコミットは行われているようです)。原理としては nspluginwrapper でやっていることと同様ですが、 Linux用の freshplayer を NetBSD (およびそれ以外のOS) に対応させるには、自分の能力ではちょっと難しいかなという感じで、考えるだけで何もしないまま年月が経過していく状態でした。

NPAPI サポート延長と Flash Player 23 動作確認

そんな中、2016年9月になって「Adobe が Linux向け NPAPI版 Flash Player の開発を継続する」というニュースが飛び込んできました (zdnetの関連日本語記事)Flash Player 23 のページには実際に NPAPI版の Linux用ベータ版バイナリがリリースされていました。早速 NetBSD + nspluginwrapper でもテストしてみたのですが、やはりというかなんというか素直には動いてくれませんでした。

ここで困ったのは「NetBSD 上で Linuxバイナリエミュレーションを使って動くバイナリをデバッグする良い方法がない」ということでした。通常バイナリがクラッシュした際に使うのは gdb ですが、 gdb は各OS別のバイナリ形式の情報がコンフィグされており、 NetBSD/i386 用にビルドされた gdbLinux の ELFバイナリを認識してくれません。かといって Linux 用の gdbNetBSD上でむりやり動かしてみるとこちらは NetBSD上で生成された core ファイルを認識してくれません。

唯一思いついて実行できたのは、システムコールのトレースを取る ktrace(1) だけでした。ただ、この記事の前半で書いたように Linuxバイナリの libflashplayer.so を呼び出す npviewer.bin は直接コマンドラインから起動されるわけではないため、 ktrace(1) でトレースを取るには次にような工夫が必要でした。

  1. 上記のツイートの中にあるように npplayer で swf 再生を起動する
  2. 起動してウインドウが出た直後ぐらいで suspend して停止させる
  3. ps(1) コマンドで npviwer.bin が起動していることを確認する
  4. npviewer.bin のプロセスに対して ktrace -p [pid] でアタッチする
  5. suspend していた npviwer.bin の実行を再開させる
  6. npviewer.bin がクラッシュして npplayer がプロセス間通信のエラーメッセージが出たら停止
  7. kdump(1) でトレースを確認

とにかくタイミングが厳しいのとウインドウマネージャーの操作が煩雑なので、意味のありそうなログを集めるために何十回も npplayer を起動してだいぶ嫌になった記憶があります……。結局、この 2016年10月までの調査はここまでで中断していました。

Flash Player 11.2 サポート切れ問題

そうこうしているうち、 2016年12月の Flash Player 24 の正式リリースで Flash Player 11.2 はもはやリリースされていないということに気づきました。

Flash Player 24 の NPAPI版プラグインが動くのであれば古い 11.2 を存続させる意味はないので当たり前なのですが、相変わらず NetBSD では動かないようなのでちょっと本腰を入れて調べるハメになりました。

どうにも煮詰まって Flash はもうあきらめるべきか、とも考えていたところ、 FreeBSD の Ports にはすでに Flash Player 24 が入っていることに気が付きました。

FreeBSD で動いているのであれば、システムコールの新規実装といった難しい修正は要らないはずです。とりあえず両者の COMPAT_LINUX 関連のソースを見比べてみたのですが、さすがに NetBSDFreeBSD とでは差分が大きすぎてよくわかりません。そもそもどこで落ちているのかも定かではないので、問題の有りそうな箇所を見つけることはできませんでした。

ただ、これだけ材料がそろってきたので、一度 pkgsrc-users@ のメーリングリスト相談メールを投げてみることにしました。するとすぐに NetBSDVM開発の第一人者でもある Chuck Silvers氏から返信をもらえました。しかし、私からの
"Is there any good way to debug Linux binary using compat_linux on NetBSD?"
という質問に対する回答は
"I think the short answer is "no". :-("
となかなか厳しいものでした。

Chuck Silvers 氏は Flash Player 11 のサポートでもカーネルを調べたことがあるとのことだったので、事前に作っていた adobe-flash-player 24 用の pkgsrc ファイル一式や npplayer を使ったデバッグ手順等々を連絡し、さらに取得していた ktrace のログも送付したところ、別途以下のような連絡を直接もらいました。

  • 実は NetBSD/amd64 -current のカーネルでは Flash Player 24 はそのまま動作する
  • i386 では Linux エミュレーションレイヤのどこかが根本的におかしい
  • /emul/linux/proc/self/maps を参照した後の SIGSEGV ではユーザーアドレス空間の末尾を参照しているが、これはスタック直後にあるカーネルだけが参照できるページテーブルであり、 linux エミュレーションレイヤーのどこかが argv や環境変数の後ろに他何かが存在すると誤って認識している可能性がある
  • スタックの設定を含め変なことになっていないか確認してみる

この返信に対して「何か必要な情報があれば取得するので連絡してくれ」と返したのですが、それを待つまでもなく 1日も経たないうちに「修正をコミットしたので NetBSD/i386 -current でも Flash Player 24 が動くようになった」との返信が。

これにはさすがに驚いて「うひょー!」と声が出てしまいましたが、このときこそがいろいろと調べてきたことが皆の協力で実を結んだ瞬間で、やっててよかったという満足感で頭がいっぱいになった記憶があります。

結局、 Flash Player 24 の動作のために修正が必要だったのはカーネル側の以下の2項目でした。

つまり、 procfs も argv も事前調査から導き出された内容と関連しており、地道な調査でデータを集めることの重要性を改めて実感したときでもありました。こうして 2017年2月末に pkgsrc/multimedia/adobe-flash-player24 のパッケージとしてコミットすることができました。

Flash Player 25 バージョン番号問題

前項の通り pkgsrc/multimedia/adobe-flash-player24 という名前でパッケージをコミットした直後の 2017年3月の Flash Player 月例リリースでちょっとした問題がありました。 11.2 同様に同じバージョン番号が続くだろうという予想に反して「同一 major version で 4か月(4回)リリースしたら major bump する」という従来慣例通りのバージョン更新が行われ、 Flash Player 25 としてリリースされてしまったのです。

リリースノートや実際の動作を見る限りでは major version を挙げるべきような変更は無く、 NetBSD上の動作も特に問題ありませんでした。今後も同様に major version が上がり続けるであろう一方で、 NPAPIサポートの延長が決まったときの経緯からして中身はそれほど変わらないであろうことが予想されたので、 pkgsrc については有識者と相談の上で pkgsrc/multimedia/adobe-flash-player としてバージョン番号を含まない名前でコミットし直すことになりました。

Flash Player 27 における "new feature" 問題

前項の Flash Player 25 の更新の後、 Flash Player 26 の更新では何事もなく、ニコニコ動画HTML5 が進んできてそろそろ惰性でのメンテになってきていました。

と、油断したところで 2017年 9月12日の Flash Player 27 への更新で問題が発生しました。

Security Bulletin の critical 表記に惑わされていたのですが、リリースノートの方を見るとしれっと
In today's scheduled release, we've updated Flash Player and AIR with important bug fixes, security updates, and new features.
などと書かれていて、この udev_has_devtmpfs に関連した何らかの新仕様が入ったようでした。

とりあえず、やれることはやるか、といろいろ調査。

前述の通り udev なるブツのサポートが追加されたことに起因している様子。

思いつくまま、過去に libflushsupport のビルドでいろいろ調べた内容を思い出しながら各種バイナリを作ってみてテスト。

このときの知見がすでにほぼ忘却の彼方という問題

週末作業でいろいろやってみたもののこれといった手がかりはなく、他からの依頼で別マシンのテストをしてたりするとあっという間に 10月になってしまい、手がかりを得られないまま次の 27.0.0.159 が出てしまいました。

が、あれだけ散々調べたのにも関わらず、 27.0.0.159 はあっさり NetBSD/i386 で動作する、という結果に。ま、こういうこともあります。

後からわかったのですが、 Flash Player 27 の初版の 27.0.0.139 は Linux でもディストリビューションによっては問題があったらしいということで、 udev サポート追加に伴うトラブルらしいというオチだったのでした。

NetBSD/amd64 バイナリの pulseaudio 対応問題

前述の Flash Player 27 での udev の問題は一応解決したのですが、これに関連して NetBSD/amd64Flash Player の音が鳴らないという問題が発生しました。これはこのエントリの前半で書いた「NetBSD/amd64Linux の pulseaudio バイナリが正常に動かない」という問題に関連します。

上記のツイートの通り、この問題を調べたのは 2015年の12月頃。その時のツイートをふたたびオレオレ Togetter するとこんな感じ。

ここでの PR は git の Pull Request ではなく Gnats の Problem Report です
再現手順では音源も指定する必要がある

というわけで投げたのが PR/50603 のバグレポート。

そして、1年半後のツイートを改めて整理したオレオレ Togetter はこんな感じ。

オレオレ Togetter もいい加減にしなさいという状態ですが、ついにブログにまとめるときが来たということで……。で、問題はここからで、従来 NetBSD/amd64 で一応鳴っていた OSS 設定でも音が鳴らなくなった、というところ。

いろいろとツイートしていたものの、結局このときも詳細はわからず、 amd64 を普段遣いしていないのもあっていったん保留にしてしまっていました。そして、新たな事実がわかったのは年の変わった 2019年 2月。

いろいろ適当なことをつぶやいていたところ、カーネルオプションとして options DEBUG_LINUX_FUTEX というものがあることがわかったので早速トライ。

というわけで、わかってみれば簡単で、 x86_64 の COMPAT_LINUXシステムコールfutex(2) のうち、 LINUX_FUTEX_LOCK_PILINUX_FUTEX_UNLOCK_PI が実装されていないため、それを使用する Linux の pulseaudio が assert() で落ちている、ということでした。

こうして 2015年12月から 3年以上経過した 2019年 3月になってようやくバグレポートに LINUX_FUTEX_LOCK_PILINUX_FUTEX_UNLOCK_PI の件を追記したところ、早速 NetBSD の主要開発者である Jason Thorpe 氏の手が挙がりました。

ただ、残念ながらというかなんというか、 Jason Thorpe 氏による futex(2) 実装の見直しは現在も thorpej-futex ブランチで作業中で、 Flash の終了に間に合わせることはできませんでした。

2021/1/4 11:34 追記

NetBSD/amd64Flash Player 以外を含む pulseaudio のバイナリを動かす場合、 OpenSUSE 13.1 の pulseaudio パッケージを assert を無効にした状態で作ってそれを使うようにすればエラーメッセージは出るものの音は鳴ってとりあえず動くようです。

rpm パッケージの再作成の詳細については OpenSUSE rpm 等で検索すると出てくるので省略しますが、とりあえずで -DNDEBUG を追加して rpm パッケージを作り直して、それを pkgsrc の pkgsrc/emulators/suse131_pulse のパッケージで使うようにすれば OKだったと思います。ただ、これを pkgsrc 公式に入れるには他の suse パッケージとの整合が取れないこと、 i386 用には公式の rpm で問題ないことから小手先で入れる方法が思いつかず、非公式オレオレ版バイナリのみとしています。

追記ここまで

Fireox 71.0 以降でプラグインが動作しない問題

長々と書いてきましたが、最後がこれ。Firefox 70.0 までは Flash Player バイナリがなんとか動作していたのですが、 71.0 に更新すると動作しなくなってしまいました。

この現象は Flash Player 側の問題では無さそうで、落ちているのは npviewer.binLinuxバイナリではなく、 NetBSDバイナリである firefox 付属のプラグイン関連の plugin-container のバイナリが core を吐いていました。

ただ、 gdb でトレースを取得してみたところで firefox の plugin 実装の詳細がわかっていないとても足も出ないという感じでした。firefox 69 あたりからの plugin 仕様の変更に関係していそうではありましたが、 バグレポートも出したものの結局今日に至るまで他の開発者からの助言を得ることもできず、これ以降の adobe-flash-player パッケージのテストは firefox68 の ESR版を使って行うことになりました。

雑多なおまけ

セキュリティ更新小ネタ

pkgsrc の adobe-flash-player パッケージを毎月のように更新してきたわけですが、 pkgsrc のルールとして「アップストリームのリリースアナウンスをコミットログに転記する」というものがあります。これがまた面倒なのですが、毎月第2火曜もしくは水曜にこれらをチェックするのがほぼ習慣になっていたので、いくつか印象に残っているものをピックアップしてみました。

2017年 6月13日
この Flash Player 26 から Security Bulletin 上での CVE番号表記がサマリからなくなり別表形式になり、 pkgsrc のログに転記しづらくなる
2017年 7月25日
Flash Player の更新と提供が 2020年末で終了することが発表される
2017年10月10日
月例更新で恐らく初めてとなる "This monthly update addresses functionality bugs." と書かれた「月例だけどセキュリティ更新じゃない Security Bulletin」が出る
(この更新はなぜか Security updates 一覧のページに載っていない)
2017年10月16日
セキュリティ更新が無いと思ったら1週間後にゼロデイ (CVE-2017-11292) に対する修正が緊急リリースされる
2017年10月25日
セキュリティ更新でない機能修正に対応する月例外更新
2017年12月12日
Security Bulletin だが "regression that could lead to the unintended reset of the global settings preference file" という「vulnerability じゃない regression だけど CVE番号つけられちゃった」みたいな微妙な修正
2018年 2月 1日
2月の月例更新前に CVE-2018-4878 のゼロデイ攻撃が発表される
(2月の月例リリースも 2月6日に前倒し
2018年10月 9日
月例更新で恐らく2回目となる "not include security fix" と書かれた「月例だけどセキュリティ更新じゃない Security Bulletin」が出る
2018年11月20日
最後(?)の定例外セキュリティ修正となるゼロデイ (CVE-2018-15981) 対応緊急リリース
2018年12月 5日
CVE-2018-15982 のゼロデイ対応による月例予定からの前倒しリリース。
なお、この Flash Player 32 への更新以降 major version が上がらなくなる
2019月 1月 8日
月例更新で恐らく最後の "not include security fix" と書かれた「月例だけどセキュリティ更新じゃない Security Bulletin」が出る
2019年 3月
月例更新でセキュリティ修正が無い場合は Security Bulletin が出ずにリリースノート記載だけになる
2019年 7月・8月
2か月連続のセキュリティ修正無し (初めて?)
2019年10月〜5月
恐らく過去最長のセキュリティ修正無し期間
2020年10月13日
最後のセキュリティ修正となる更新 (Edge, IE 版最終バージョン)
2020年12月 8日
最後の月例更新。
フェードアウトということなのか、11月と12月はリリースノートページの更新がバイナリ公開より半日ほど遅く、コミットログは暫定で記載した

2019年になるとセキュリティ更新のない月が増えてきました。これについては、いよいよ出尽くしてきたということなのか、Flashの終焉が現実的にも見えてきたのでセキュリティ研究者あるいは Bounty Hunter たちのターゲットから外れたということなのか、どちらなのかはわかりません。ただ、20年を超える歴史を持つソフトであるにも関わらず毎月のようにセキュリティホールが見つかるというのは、複雑なソフトウェアにおいて基本設計が如何に重要であるかということを考えさせられる事例ではあります。

libflashplayer.so のバイナリの中身

Adobe が提供している libflashplayer.so のバイナリですが、様々なカーネルバージョンが存在する各種の Linux ディストリビューションに対して単一のバイナリで対応しているというのは一体どうやってビルドしているのだろうか、という疑問を持ったことがあります。

というわけで libflashplayer.so のバイナリの中身を適当に見てみると
GCC: (GNU) 4.3.2 20081105 (Red Hat 4.3.2-7)
という文字列があり、 Red Hat 6 あたりの相当古い環境でビルドされているようです。(Linux は詳しくないので見当外れでも笑ってスルーしてください)

libflashsupport.so のバイナリはライブラリなので関数や変数のシンボル名もそのまま残っているため、 strings(1) コマンドで見るだけでもそこそこ情報を得られたりはします。たとえば、問題解決の中で挙げた Flash Player 26 と 27 の strings(1) の出力の差分を見るとこんな感じです。








ざっと斜め読みしただけでも、 udev の追加、意図したものかどうかよくわからない pulseaudio 関連の追加、さらにはアップデート確認の各国語メッセージが入っていたりして、そこそこ大きな変更である一方、雑に diff リストを見比べられるくらいには変更が少ない、とも言えます。

まあ、これらは情報というかヒントに過ぎず、 NetBSD での Linuxエミュレーションの問題で役に立つかというと、そんなに簡単な話ではないのですけれど。

Flash Player の今後

いくつかのページでも言及されていますが、公式ページにも記載があるとおり、 2021年 1月12日以降は Flash Player のセキュリティ更新がされないだけではなく、 Flash Player 自体の実行がブロックされるようになってしまいます。

ただ、2020年末での Flash Player サポート終了が決定したのは先に書いたとおり 2017年 7月です。では、いつ頃のバイナリから 2021年 1月以降の実行がブロックされるのだろうかとちょっと見てみたところ、2020年2月リリースの 32.0.0.330 であれば 2021年以降も実行可能なようでした。

  • 32.0.0.433 (最後のセキュリティ更新版)
    サポート終了案内ページへのリンク画像表示になる
  • 32.0.0.387
    これもサポート終了案内ページへのリンク画像表示になる
  • 32.0.0.344
    無言で実行されないっぽい?
  • 32.0.0.330
    動作しているっぽい?

※2021/12/12追記

nspluginwrapper 付属の npplayer を使う場合は 32.0.0.371 でも動作するようです。

追記ここまで

今回 Flash は終わりを迎えますが、自分自身が数年間やってきた作業について将来懐かしむことがあるとすれば、このブログエントリとともに古いバイナリを引っ張り出してきて再度感慨に耽りたいと思います。

最後に

このような長文駄文を最後まで読んでいただきありがとうございました!

「タイニーあぴミクさんにせまられたかった。」 for PC-6001 バイナリ

前回の Tinyみずいろのバイナリ に続き、P6用タイニーあぴミクさんデモのバイナリもアップしました。

PC-6001(16KB)実機およびエミュレータで実行可能ですが、エミュレータでの実行の場合は実機と動作が異なる場合があります。

以下はダウンロードバイナリ付属の README.md の html版写し:
http://www.ceres.dti.ne.jp/tsutsui/p6/tiny_apimiku/README.html

タイニーあぴミクさんにせまられたかった。 for PC-6001

http://www.ceres.dti.ne.jp/tsutsui/p6/tiny_apimiku/tiny_apimiku.zip

これはなに?

ニコニコ動画にアップされていたマシンガンP のあぴミクさんつま先立ち動画のBGMをOSC京都のデモ用に耳コピしていて、ふと
MMDミクさんを PC-6001で表示したらどんな感じになるんだろ?」
と試してみたら意外とそれっぽかったので、ついこんなものを作ってしまいました。

ニコ動のデモ動画では PC-6001(32K) 用に作成したバイナリを動かしていましたが、このアーカイブに含まれるバイナリは16KB対応版です。

内容物

  • tiny_apimiku16k.wav :
    実機用テープセーブデータ音 wavファイル BASIC+機械語
  • tiny_apimiku16k-bas.P6 :
    エミュレータ用テープデータ P6 形式ファイル BASIC部分
  • tiny_apimiku16k-bin.P6 :
    テープロード速度変更可能なエミュレータ用テープデータ P6 形式ファイル 機械語部分
  • tiny_apimiku16k-bin-emu.P6 :
    テープロード速度変更不可のエミュレータ用テープデータ P6 形式ファイル 機械語部分
  • README.md :
    このファイル

実機デモ動画

実機デモ動画は以下のニコ動にアップしてあります。

エミュでの動かし方

  1. テープロード速度を変更可能なエミュレータ (PC6001VX等) の場合は、速度を 900bps 相当 (※1) にしてください
  2. 機種設定および起動時の MODE (機種選択) は PC-6001(16K) または PC-6001(32K) を選択
  3. How Many Page? は 2 を入力
  4. エミュレータのテープファイルとして tiny_apimiku16k-bas.P6 を選択 (※2)
  5. "CLOAD" で BASIC プログラムロード
  6. ロードが終わったら次のテープファイルとして
    tiny_apimiku16k-bin.P6 (テープロード速度変更可能なエミュレータの場合) または
    tiny_apimiku16k-bin-emu.P6 (テープロード速度変更不可のエミュレータの場合) を選択
  7. "RUN" で BASIC プログラムを実行 →機械語パートのロード開始
  8. デモスタート! (※3)

※1
このデモでは「テープをロードしながらPSGでBGMを演奏」を実現するために、
「テープセーブデータ中のストップビットを増やす」
という手法を使っています。
しかし、エミュレータ用の P6ファイルにはストップビット数に関する情報は記録されていないため、本来のデータだけをロードすると待ち時間が足りずに表示がおかしくなってしまいます。
エミュレータではテープのロード速度自体を変更することでズレを回避することが可能です

「テープセーブデータ中のストップビットを増やす」の手法については Tiny野郎さんのブログ記事 「P6イース2オープニングの作り方(6)」に解説があります。

※2
テープファイルの指定方法について、PC6001VX の場合は以下のとおりです

  • テープの P6ファイルをデフォルトの ~/.pc6001vx/tape 以下に置く
  • PC6001VX を起動 → マウス右クリックメニュー → 「TAPE」 → 「挿入」
  • 開いたファイル選択ダイアログで P6ファイルを選択して「Open」を押す

※3

  • 「テープロード速度変更不可のエミュレータ」用の tiny_apimiku16k-bin-emu.P6 ではストップビットが少ない分だけロードが速く終わってしまう対策として適当にダミーデータを挿入してあります。
  • テープロードおよび PSG音源ドライバの 2ms割り込み以外のROM内ルーチンは使用していないので、互換BASIC ROMでも特に問題なく動きます。

実機での動かし方 (いったんテープに録音)

  1. PCの音声出力端子(ヘッドホン端子等)とデータレコーダーの REC 端子を接続 (※4)
  2. 録音用カセットテープを入れてして適当に先頭の録音可能位置にセットして待機
  3. PC側でセーブデータ音 wav を再生すると同時にデータレコーダー側で録音を開始 (※5)
  4. テープが用意できたらテープが現物になる以外は「エミュでの動かし方」と同じです

※4
たいていの場合 PC側はステレオ、データレコーダー側はモノラルですが、L側だけの接続でも問題ありません。

※5
PCのオーディオドライバやプレーヤーによってはデータレコーダーのピーガー音をノイズと判断してミュートしてしまうものがあるようです。
その場合はマニュアルその他を確認してミュート等を無効に設定してください。
また、各種音響効果はロードに悪影響を及ぼすのですべて無効にしてください。


実機での動かし方 (wav直接ロード)

  1. PCの音声出力端子と本体に接続したデータレコーダーケーブルの再生端子を接続 (※6)
  2. 「エミュでの動かし方」の説明の "CLOAD" まで進めたら PC側で wav の再生を開始 (※7)
  3. CLOAD が終わって「カチッ」のリレー音がしたら PC側の wav 再生を一時停止
  4. "RUN" を実行して、再度「カチッ」のリレー音がしたら PC側での wav 再生を再開
  5. あとは「エミュでの動かし方」と同じです

※6
PC側のステレオ端子にデータレコーダーケーブルのモノラル端子を挿すとR側がGNDショート状態になります。
たいていのヘッドホン端子は地絡を考慮して保護抵抗が入っているので壊れることはないと思いますが心配な方はステレオプラグ⇔モノラルジャック変換ケーブルを用意してください。
また、PC音声出力端子を使うと再生音モニタができないため、リレー音で開始終了を判断してください。

※7
再生音量については適宜調整してください。
うまくロードができない場合は、「いったんテープに録音」の※5 の注記にあるとおりPC側のドライバや再生ソフトの設定も確認する必要があります。


実装解説

どうやって作ったの? についてはブログ記事にまとめています。

謝辞

  • マシンガンP様
    かわいいあぴミクさん動画に癒やされるだけでなく、こんなものを作るきっかけをいただきました。
  • TINY野郎さん
    なんでもピーガー、および、PSGドライバ用MMLコンパイラを活用させていただきました。
  • エミュレータ・ツール作者のみなさん
    この時代にP6がこんなに楽しめるのも各開発者の方々のおかげです。

みなさんありがとうございました!

作者について