crystalcandyまで修正完了。
*
あとはfg4cppdemosを直せば動作確認までできていい感じになる。
できれば今日中に完了したいところだが、そううまくいくだろうか。
あかん、サイトちょっといじりたくなってきた
*
だってわざわざタグ打つのめんどいし。
日付入力するのもめんどいし。
そのためにはまずログインシステムを構築する必要があるが、とりあえずは元からあるやつを使えばいいのでは、という気もする。
後から差し替えるというのでも問題ないはずだ。第一ログインするのは私くらいのものだ。
まー、動作確認まで終わってからにしよう。途中で他のことを考えてもしかたがない。
結局fgppをやっつけた。
*
fg4cppdemosを修正しようと思ったらfg4cppを修正する必要が出てきたし、fg4cppはfgppに依存しているから、fgppを先に修正した方がすっきりするだろうという結論。
次はfg4cppをやっつけて、その次にfg4cppdemos。その辺が完了したらsucroseとcrystalcandyをやっつけて、動作確認し、なんか問題あったら直し、最終的にpushして完了、という流れ。
寝るまでにはfg4cppdemosまでやっつけてしまいたいところだが、やる気がもつかしら。
fgのダミー関数の定義自動生成はやっつけた。
*
中途半端な時間だしfgppの方もやってしまおうかどうしようかと考えながらうだうだしていたら、もう寝た方がよさそうな時間になっていた。
やる気がからっぽになっていたようだ。起きたらがんばろう。
fgのダミー関数用ソースをC++からCにしたら、ダミーの共有ライブラリのサイズが小さくなった。得した気分。
ちなみにまだpushはしていない。インターフェースは変更していないのだがヘッダファイル名とか変えてしまったので、sucroseやcrystalcandyまで修正しないと動作確認ができないのだ。
や、違うな。依存プロジェクト全部だからデモプロジェクトもそうだ。
うんそうだな。次はfgppじゃない。fgppはfgと無関係だし。fgに依存しているプロジェクトを順々に修正していき、動作可能な状態まで持っていこう。
ダミー関数の定義を自動でやるべきな気がしてきた。
*
その関数がポインタを返すのか、数値を返すのか、何も返さないのかでダミー関数の処理は一律に決まるわけだし、ヘッダのインターフェースを修正したらダミー関数の方も修正する、なんてのがまず非効率なのだ。
それに今、C++で記述していたfgのダミー関数定義をCに直しているのだが、コンパイル時に関数定義の引数名が記述されてないよエラーが出て困っている。
インターフェースの引数名を記述するなら、ダミー関数を定義するソースファイルではなくインターフェースを定義しているヘッダファイルに書くべきだ。それは引数がどういう意味を持っているのか示す重要な情報であり、それをソースファイルだけに記述するというのはもったいない。
インターフェースについてのドキュメントを書くつもりはないのだし、記述しなければならないことでそれが情報になるのならなるべく書いておきたい。
*
しかし、fgのイベントハンドラの定義をちょっと直して、Cでもイベントドリブンなプログラムを書きやすくしたというのに、threads.hが存在しないためにfg4cppdemosのソースを移植できなくて困っている。
glibcのバージョンを上げるべきなのかしら。しかしどこまで上げればC11に対応するのか分からん。ぐぬぬ。
ゲームを動的ライブラリでなく、単体のプログラムにするべきではとか考えていた。
*
現時点での結論としては、それはやっぱだめっぽいといったところ。
正直、単体で動作可能な方が分かりやすくていいのだけれど。
しかし、その場合ゲームから動的リンクするライブラリを選択できないわけで。
ある機能の利用を許可しない場合に、同じインターフェースを持ったダミーライブラリにリンクさせることにより、ゲームの動作自体は可能にしても特定の機能の利用を不可能にする、みたいなことをやりたいのだ。
この機能の実現のためには、ライブラリを勝手にリンクされては困る。
最終的には、SELinuxみたいなセキュアOSの機能も利用する形できちんとやりたいところ。といっても、まだSELinuxとかのセキュアOSを使ったことないけど。ただの妄想である。
windows側の対応が予想以上にやる気起きなかった。
*
C++11の対応がクソすぎるのが悪い。char16_tとかUnicodeリテラルとかに対応してないのをどうにかするとかめんどすぎるので、その辺とかC++11対応がちゃんとしてくるまで待つ。
vs2014はやくしろ。
*
というわけで別のところを進める。
どこを進めたものかと考えたところ、音やゲームパッドなど、インターフェースの追加を進めるのも悪くないだろう。
しかし、現時点でウィンドウの生成と、それに付随してマウスやキーボードの入力、あとOpenGLでの描画にも対応できているわけだ。
それならば、運用側の処理についても進められそうな気がする。
そうするとそれはそれで、インターフェースの追加が必要になってくるけど。
イベントハンドラのインターフェースを修正した。
*
本当なら昨日に終わっていそうなものだが、びみょうにやる気が出なかったのだ。
*
raspberrypiにMinecraft Pi Editionをつっこんで試してみた。
マップが比較的小さいとか、クリエイションモードしかないなどの制約を除けば、快適に動作する。名刺サイズであることを考えれば、なかなかすごいと思う。
しかし、それが生かせない場面はきつい。具体的にはデスクトップからしてきつい。軽量なawesomeを使っているが、端末のurxvtを開くだけでも数秒かかるし、ウィンドウを動かそうものなら描画が遅れまくる。
Xを起動しなくてもOpenGLESを使えることは知っているのだが、その場合キーやマウスなどの入力を取得するのはどうすればいいんだろう。そこが解決すれば、raspberrypiをゲーム機にできそうなのだけれど。
*
次はどこを進めようか。windows側の対応を進めようかなぁ。OpenGLの関数の取得らへんが気になるし。
OpenGL対応、一応できた。
*
拡張機能とかは全然対応していないが、基本的な骨組みはできた感じ。
気になるのは、まだ対応していないwindowsでの実装か。OpenGLの関数の取得をコンテキスト生成後に行なうようにしているので、問題ないと思うのだが。
*
イベントハンドラ周りのインターフェースを修正する。
SDLとかで、なぜイベントドリブンなインターフェースがないのかと考えたところ、多分修正しにくいからだと思った。
ただの関数であれば、別の関数を作るとかすれば、既存のインターフェースを壊さずに拡張できるが、イベントハンドラはそうもいかない。あるいは、引数を末尾に付け足すとかなら可能かもしれないが確証はない。
で、どうすればいいの考えたところ、イベントハンドラの引数は完全固定にして、引数に対し関数を使って値を取り出す形にすればいいのだ。
具体的には、イベントハンドラの引数を、それ専用の構造体の参照1つだけにしてしまう。構造体の実装は隠蔽し、データ取得は関数を使う。イベントハンドラの引数をいじってインターフェースを壊したりせずに、データを増やしたり減らしたりできる。
どっかで見たなぁと思ったらJavaのAWTとかSwingとかのイベントリスナのような感じだ。あんな感じに修正しようと思う。
まだOpenGL対応作業中。
*
ようやくインターフェースが定まってきた。
OpenGLのインターフェースは、OpenGLを使える状態にしていなくてもOpenGLの関数を呼び出すことができてしまうのが気に食わなかった。
OpenGLを使える状態というのはつまり、スレッドに対してウィンドウとコンテキストの関連付けを行なった後、ということ。
それをする前は、そもそも関数呼び出しを書くことができない、というインターフェースにしたかったので、そのようにした。
具体的には、ウィンドウとコンテキストの関連付けを行なう関数の戻り値に関連付け情報を返し、glClear()などのOpenGLの関数の第1引数に関連付け情報の参照を渡さないといけないようにした。
無理矢理やろうとすれば、適当なデータを関連付け情報の参照にキャストして渡したりすればできなくもないが、そんなあほなことはやらなければいいといった感じで。
*
他にも、なんたらした後じゃないと実行できちゃまずいのに、なんたらする前でも実行できる状態になってる、みたいな点を改善して、大体満足のいくインターフェースになったと思う。
問題なければ、後は実装を書くだけだ。問題があっても、それが発覚した時点でまた構成を考え直すだけのことだが。
多分、一番めんどくさいのはOpenGL関数の取得。バージョン毎に取得する関数を変えないといけないし。処理の流れ自体は変わらないだろうから、とりあえずは1.0だけ対応するとかいう感じにしようかな。
とりあえずプロジェクト名を変更した。
*
今まで使っていたプロジェクト名は管理用インターフェースのディレクトリ名にした。で、新しい名前空間をゲーム用インターフェースのディレクトリ名にして、プロジェクト名はそっちにすることにした。
ソースの方も名前空間とかincludeとか型とかの記述を全部置換した。ビルドが通って、デモが動作するところまでは確認したけど、量がなかなか多かったためなにかミスしてないか不安だ。
まー、ミスってたら直すだけのこと。気にしても仕方がない。
*
起きたら、さっそくOpenGL対応を進めよう。片付いたら、今まで作った部分も見直していこう。
OpenGL対応を進めている。
*
ちょっと方針を変えるべきかもしれない。作り直しとかは必要ないんだけど。
インターフェースの定義とそれを実装するライブラリ、ライブラリを運用するプログラムはそれぞれ別にしているけど、その依存関係を変えるべきかも。
ライブラリとプログラムはどちらもインターフェースに依存させているが、やはりプログラムはライブラリに依存させる方がいいかもしれない。
ライブラリを運用するプログラムから、ライブラリを使用するゲームを実行するわけだけど、同じインターフェースを提供すると困ることがある。
基本的に、プログラムからゲームを実行するという関係からも分かる通り、プログラムはゲームより上位の存在なのだ。プログラム側ではできるけど、ゲーム側ではできない、というような処理を存在させたい。
簡単に言えば、プログラム側で生成したものをゲーム側に渡し、それをゲーム側で使用して色々させたいが、生成自体をゲーム側でやらせたくないだとか、渡したものをゲーム側で勝手に破棄させたくないだとか、そういうこと。
*
そのためには、そういったプログラム側でやらせたい処理のインターフェースはライブラリの中だけに存在させ、ゲーム側も利用するインターフェースには存在させない、というようなことが必要になってくる。
なので、プログラムはライブラリに直接依存させた方がいいかな、と考えたわけだ。
でもそれは早計かもしれない。もっと考えたら色々案が出てきた。
例えば、インターフェースプロジェクトを2つに分ける。アプリ用と管理用の2つに分け、アプリ用はゲーム側が使い、アプリ用をカバーする管理用はプログラム側が使う。
プロジェクトを2つに分けなくても、処理を実装するライブラリを別々にするという手もある。それが一番手っ取り早いかもしれない。
で、そういうことをするとなると、今まではまぁいいかで済ませていたものも、分割するべきな気がしてきた。
*
とまぁ、このようなことをなぜ考え始めたかと言えば、OpenGL対応が関わってくる。
OpenGLのパラメータは、ゲーム側に設定させたくないのだ。
パラメータ自体は必要であり、それを使用したOpenGLの初期化、つまりコンテキストの生成はゲーム側で自由にできるようにしたいが、パラメータの設定はゲーム側でやらせたくない。パラメータ自体は、プログラム側が生成した1つだけがあればよく、ゲーム側は生成も破棄もする必要がない。
そのような要件を満たすにはどうしたらいいか考えたところ、そんな感じにする必要がありそうだな、となったわけだ。
さて、どこから取り掛かろうか。
linux用のウィンドウ制御できた。
*
前に作ったやつは、ウィンドウのイベントを処理するスレッドとは別に描画用のスレッドを作るという、1つウィンドウ作っただけで2つのスレッドが走り出すような感じにしていたんだけど、それをやめた。
ウィンドウのイベントを処理するスレッドだけにしたので、記述が単純になった。
描画用のスレッドなんてのは、ライブラリ利用側でどうにかすればいいのだ。
*
C言語のインターフェースのC++ラッパーを使って、動作確認用のデモを色々作っていたのだが、せっかくなのでC言語のインターフェースをC言語で直接叩いてみようかと思ったがやめた。
C言語のインターフェースでは、ウィンドウのイベント処理を行なうイベントハンドラを関数ポインタでしか処理できない。そしてその関数の引数に、余分なデータはない。
C++ならstd::function、ファンクタで処理する形になっているので、関数の引数以外のデータを持たせられるのだが、C言語のインターフェースではそれができないのだ。
つまり、複数のイベントハンドラ間でデータを共有したり受け渡したり、といったことが、グローバル変数を使うしか方法がない、という感じになってしまっている。ごみくずである。
C言語のイベント周りのインターフェースが無駄になってしまった香りがする。C/C++以外の言語に対応する場合も、多分C++のファンクタを使ったインターフェースを使うだろうし。まぁいいか。
*
というわけで次は運用の方に関わってくるウィンドウ、メインウィンドウにとりかかろうと思ったが、気が早いかもしれない。
だって、そんな特殊なウィンドウ作ったところで、まだウィンドウに描画できないんだもの。先にOpenGLをやっつけるべきか。
OpenGLコンテキストに関係する部分は大して量がないはずだし、OpenGL関数についての部分はほぼ使い回せるはずだし。ぱぱっとやってしまうか。
あ、ウィンドウを属性や位置を指定して生成する関数作ってなかった。まぁいいか。必要になったら追加しよう。メインウィンドウでサイズ固定ウィンドウが必要になるだろうし。
開発は順調に進んでいる。
*
構成とか、だいぶいい感じになってきている。
言語別のインターフェースの定義、その実装、その運用をそれぞれ別プロジェクトに分けた。
インターフェースを実装した運用プログラムであれば、その実装ライブラリがなんであろうと、動かせるような感じになっている。
インターフェースの定義プロジェクトには、実装が空のソースを含んでいる。
そのソースをビルドして作った動的ライブラリを使えば、実際に実装したライブラリがなくても運用プログラムの開発を行なうことができる。
実装が空なので、それを使って実行してもまともに動かすことはできないが、実行時には実際に実装したライブラリをリンクすればいい。
インターフェースを同一にすることで、実装と運用を完全に分離することが重要なのだ。
インターフェースを定めてしまえば、その運用と実装を、完全に別々に開発することができる。
*
今のところ、インターフェースプロジェクトはCとC++の2つを作っている。
C++のインターフェースから、Cのインターフェースを実装したライブラリを使うためのラッパープロジェクトも作っている。
Cのインターフェースは関数名の重複ができなくて扱いにくいためだ。
最初からC++のインターフェースを実装したライブラリを作ればいいと思うかもしれないが、CのインターフェースをC++から利用できるように、Cのインターフェースで作っておけば他の言語から利用しやすいのだ。
後々、D言語やその他の言語用のラッパーも作ろうと考えている。
*
それで、今はウィンドウ周りの実装を作っている。前に作ったものを流用しているが、処理を見直して、より単純な形にした。前に作ったものは余計に作り込みすぎていた点があったのだ。
それが終わったら、運用の方に関わってくるウィンドウについて作ろうと考えている。どのように実装するかは、まだもやもやしているが。
dlopen()とかLoadLibrary()とかの挙動を確認した。
*
linuxの.soファイルには未定義シンボルが存在していても、実行時に解決することで問題なくできるんだけど、windowsのdllはそうはいかなくて残念。
LoadLibrary()を使って実行時にリンクするにしても、どの関数がなんて名前のライブラリに含まれるか、などをビルド時に確定しなければならないというのが残念だ。
とはいえ、そこまできつい制約というわけでもないのでいいけど。
*
ゲームを作る際に使うのはヘッダファイルだけ(windowsの場合はインポートライブラリも必要)、ヘッダファイルに記述されている関数や構造体の実体は、ゲームを起動するためのプログラムの方にライブラリとして実装する、みたいな感じで。
うん、イメージできてきた。根底のインターフェースはCで用意しておいて、それをラッピングすることにより様々な言語に対応できる形にしよう。今度はちゃんとできるはずだ。
次の段階に進んで気付いたことは、どうやら構成が間違っているらしい、ということだった。
*
早い話が、また作り直しである。だがその前に検証しておかなければならないことがいくつかあるので、それらを試してから。
ハードウェア周りの抽象化としてdpを作り、その上にもう1つライブラリを重ねてそれ以外の機能に対応、そのライブラリとdpを使って作る、という感じをイメージしていたが、ハードウェア周りとそれ以外の機能を分けることにあまり意味がない感じがした。
逆に、名前空間を分けていたりして扱いにくいのだ。
よく考えたら、ライブラリの機能を使う、ゲーム部分は単体で動作できなくてもいいわけだし、そうなると機能の実装を行なうのも別の場所でいいなぁ、とか。
*
そんな感じで色々考えた結果、またしてもプロジェクト作るところからやり直した方がいいんじゃないだろうか、という印象。
というわけでまずは機能の検証から。
*
ライブラリの名前も変える。dpの上に重ねる予定だったライブラリの名前を使うことにする。
luaスクリプト見直していたら、やはりというか私が悪かったようだ。
*
スクリーン切り替え処理がグローバルではなく各ウィンドウに対する処理として設定していた。
ディスプレイにウィンドウが存在しないといかんというのも仕方がないというものだ。
バージョンを上げることになんとか成功したので、それに合わせて修正を加えていたら気が付いた。
*
とかいうのを、前の日記を書いて、起きた後に作業してたら判明したので書いていたんだけど、途中で書くのやめて今日まで忘れてた。
*
どうにも、どう進めたものか、どう設計したものかイメージがまるでまとまらなかったのだけれど、ようやく見えてきた。
とりあえずは、直接起動機能を作ることにした。ランチャのようなものは、それで起動したものとして実装するような感じにする。
その辺から考え始めたら、ディレクトリ構成とか、起動に必要なデータとかが見えてきた。
見えてきたのはいいのだが、機能を実装するとなると、その機能を実装するために必要な機能がたくさんある。
完成は遠そうだなぁ。
awesomeのluaスクリプトをいじくっていた。
*
今までもちょこちょこと、デフォルトのものに修正を加えていたが、マルチディスプレイ周りがびみょうだったこともあり、全体を見直して、大幅に書き換えることにした。
それで、大体満足な感じになったのだが、どうもawesome自体にバグっぽい仕様があるような雰囲気がする。
ディスプレイに1つもウィンドウが表示されていない場合に、別のディスプレイのフォーカスを移すことができないのだ。
ターミナルを1つでも起動するなりして、ウィンドウを作れば問題ないので、致命的とは言えないが。
しかし、ディスプレイに1つもウィンドウが存在しない状態というのは、つまり別のディスプレイに切り替えるということも十分考えられる操作なわけで。不便だ。
バージョンを上げれば改善されるかもしれないと思ったのだが、なんか知らんがビルドがこける。/dev/dri/card0に対して権限がないらしいのだが。chmodで無理矢理権限を付加してもだめだし、そういうことではないのかもしれない。
*
でもまぁ、タイル、フローティング、フルスクリーンの3つの状態を効果的に使えるようにできたので、大体満足。
生成されるウィンドウがデフォルトでフローティングとかいう、タイル型ウィンドウマネージャにあるまじき挙動になってるが。
でもまぁ、一発でディスプレイ内に表示中のウィンドウをタイルに変更したり、フローティングに変更したりできるようにしているので、問題はない。
正直、タイル状態をうまく活用できるプログラムは大して多くないのだ。ブラウザにしたって、音楽プレイヤーや動画プレイヤーにしたって、フローティングの方が扱いやすい。
ターミナルをタイル状態で並べるのはかなり便利なんだけど。開発が非常にやりやすい。
ディスプレイ制御、インターフェースを変えた方がよさそう。
*
ディスプレイの解像度や回転方向を変更することはできるのだけど、どうもおかしいので調査していた。
おかしいのは、回転方向を変えた後、例えば1600x1200を90度回転させて1200x1600にした場合、向きは変わっているのだけど、表示自体は1600x1200のままになってしまっている、という点。
90度回転しているにも関わらず、表示内容は1600x1200のままなので、実質的には1200x1200のようになってしまっている、とでも言うべきか。
前から気になっていたので調査していたのだが、原因は大体分かったものの、きちんと解決するためにはインターフェースの変更も視野に入れた方がいいことが分かった。
*
原因としては、各ディスプレイの解像度とは別に、スクリーンサイズというものがあり、そのスクリーンサイズを変更していなかったのがいけなかったようだ。
複数のディスプレイを組み合わせて1つの大きな画面を構成する場合、最終的にできあがる画面のサイズを決める必要がある。例えば、1600x1200のディスプレイ2つを横に並べる場合には、3200x1200、というように。それがスクリーンサイズだ。
スクリーンサイズを決定してから、各ディスプレイに対し、その中のどの部分の表示を担当するか決定する、という流れになるわけだ。1600x1200のディスプレイ2つで考えると、一方を1600x1200+0+0、もう一方を1600x1200+1600+0、という感じ。
しかしながら、私が作った処理では、スクリーンサイズの変更をしていなかったため、うまくいっていなかった、ということらしい。
スクリーンサイズ3200x1200の中に1600x1200+0+0と1600x1200+1600+0の2つのディスプレイを設定していたものを、1600x1200+0+0と1200x1600+1600+0にしようとするならば、まずスクリーンサイズを2800x1600にしなければならない、ということだ。
スクリーンサイズを変更しなかった場合、ディスプレイの解像度を1200x1600にしたところで、スクリーンサイズの高さは1200までしかないわけで、下方1200x400は使うことができない、とかそういうことだ。
*
しかし、現状のインターフェースでは、1つのディスプレイの解像度を変更することしか考慮していないため、スクリーンサイズ、つまり表示全体のサイズを算出したりすることが困難だ。
そもそも、スクリーンサイズという概念が存在することを考えると、1つのディスプレイに対して処理を行うべきではなく、マシンで扱えるディスプレイ全てというか、表示に関わるディスプレイ全てに対し、同時に処理を行うべきなのだ。
ということになってくると、やはりインターフェースからして考え直さざるを得ない。正直、現状のインターフェースはしっくり来てなかったから、後で変更しようとは思っていたからいいけど。
らでおんのオープンソースドライバを導入した。
*
前試した時はうまく動かなかったのだが、今回はきちんと動いてくれている。
サブディスプレイの90度回転しても問題ないし、windowsが必要ない場合はこれでいってみよう。
現時点ではOpenGLが2.1までしか使えないのは気になるところだが、きっと開発が進めば対応バージョンも上がるだろう、多分。ハードウェア的には、OpenGL4.3まで対応可能なはずなのだ。
なにより、他のオープンソースドライバと共存可能なのがいい。fglrxを使うと、drmからして有効にできないからなぁ。
他のオープンソースドライバと共存可能ということはつまり、windows上のvmwareで、ディスクを物理マウントして起動すれば、そのままvmwareのGPUアクセラレーションが有効になり、GUIも使えるということだ。これは強い。
*
TuxOnIceを使ってハイバネーションに対応しようとして諦めた。どうにもうまく動かない。
搭載メモリ=16GB分のスワップパーティションとか作るのめんどうなので、ファイルでやろうとしたがうまくいかなかった。
そもそも情報が少なすぎるのだ。検索して出てくる情報は、なんだか古いものばかりな気がする。設定ファイルのパスとかどれもまちまちだし。
スワップパーティションを作ることができれば、TuxOnIceなぞ使わずに、最初からカーネルに存在する機能でハイバネーションできるはずなのだが。
スタンバイは問題なく動くようなので、とりあえずはスタンバイでもいいかという気持ち。電力供給を止められないのは気になるけども。
とはいえ、普通に起動してもかなり速いんだけどね。どちらかというと、作業状態の保持をやりたいのだ。
*
あと、今までは離席する時にディスプレイの電源を切るだけで、電源を入れれば誰でも使える状態になっていたのだけど、そこも改善した。
gnome-screensaverを使い、画面のロックをできるようにした。
これで、離席する前にロックすれば、ロック解除しなければ使える状態にならないようになった。安心安心。
*
そんな感じで環境をいじくっていたので、全く進んでいない。
起きたら本気出す。
truncate対応した。
*
これでファイル入出力もできるようになったし、次の段階に進むべきか。
足りない機能はまだまだあるが、それは必要になった段階で付け足していくことにする。
読み書き追記、大体対応できた。
*
readにwrite、seek、tellに対応したので、読み書き、ファイルポインタの移動、現在位置取得ができるようになったわけだ。
しかし、あと1つだけ対応できてない機能がある。truncateだ。
*
truncateの機能は切り捨て、切り詰めだ。ファイルを指定した長さに切り詰め、以降のデータを存在しないものにする。
この機能を使えない場合に同じことをするなら、1度ファイルの内容を全て読み込み、fopenのモード指定でwやw+を指定することでファイル内容をクリアし、切り詰めない部分を書き込む、などという感じのことをしなければならない。なんともめんどくさい。
しかし、実際に用意されている機能は、切り詰めた後の長さを指定するだけのもので、微妙に信頼性に欠けている。
どういうことかと言えば、現在のファイルよりも長い長さを指定することも可能なのだ。その場合、増えた部分には\0の文字で埋められるか、エラーが発生するかする。
エラーが発生するかどうかは、ファイルシステムに依存するらしい。XSIとかいう仕様に従っているらしい、linuxのネイティブなファイルシステムでは確実に伸長されるようだが、例えばvfatのような、非ネイティブなファイルシステムではエラーになり、伸長されないらしい。
通常linuxでvfatを使うことなどあまりない、と言いたいところだが、例えばUSBメモリなどのリムーバブルメディアのファイルシステムは大体vfatのはずだ。なんにせよ、ファイルシステムによって失敗する可能性のある処理というのは面倒なものだ。
そもそも、truncateは切り詰めるための機能であり、伸長するための機能ではないのだ。失敗する可能性があるところからして、おまけと考えるべきだろう。伸長したいなら書き込めばいいのだ。
*
というわけで、伸長を許可しないインターフェースを用意したいところなのだが、どうしたものか。
例えば、削りたい長さを指定して、その長さ分末尾から切り詰める、というものを思い付いたが、正直使いやすそうとは思えない。
その長さを算出するためには、切り詰めた後の長さと切り詰める前の長さを把握してなければならないからだ。
win32APIにはSetEndOfFile()というものがあるが、これはファイルポインタの現在位置まで切り詰める、というものだ。
しかし説明を見れば分かる通り、ファイルポインタをファイルの終端より後の地点に移動させておけば伸長も可能な作りになっている。別にファイルポインタは、始端と終端の範囲内しか指せないという決まりはないため、そこでは縛りを作れない。
あるいは、truncateのインターフェースはそのままで、伸長は推奨しない、という形にするのもありか。とりあえずそれでいこう。
おかしい。完成してない。おかしい。
*
18日の日記、直前の日記には、今日中にはlinux側の実装を片付けてしまいたいところなんて書いてあるのだが、未だ出来ていない。おかしい。
その理由の1つとして、若干やる気が削がれているというのがある。なんでだろう。
なんでだか分からないがやる気というのは出るものではなく出すものというのが私の意見であるので、今現在出ていない理由なんていうのは割とどうでもいいのだが。
2つ目の理由として、予想していたよりもややこしいことになっている、というのがある。
ファイル入出力は、前の日記に書いた通り、読み込み用に開いたなら書き込み関数の呼び出しがコンパイルエラーになる、書き込み用に開いたなら読み込み関数の呼び出しがコンパイルエラーになる、という仕様にしようと考えている。
しかしながら現実的には、用意されているAPIにそのような制約が存在しないパターンがほとんどだ。
開くとファイルポインタが得られ、それを使って読み込み関数を呼び出せば読み込み処理が行なわれ、書き込み関数を呼び出せば書き込み処理が行なわれる。オープン時の読み込み、書き込み指定と処理が合致していなかったりした場合、エラーとなり処理は失敗する。大体そんなもんだ。
そのようなAPIを使って今回の要件を満たすならば、内部的にはどのような開き方をしても読み込み、書き込みの両方を行なえるようにしておき、型によって可能な処理を縛る、というような感じになる。
今まで作ってきた機能よりも、環境が異なっても変える必要がない、処理を共通化できる箇所が増えたため、それを考慮した構成になるよう、コードを組むのに苦心している、といったところか。
あまりに考えすぎると、やる気がどんどん削がれていくのだ。考えすぎると手が出なくなってしまうわけだ。
*
まー、起きたらやっつけるとしよう。寝る前に、どこから攻めるかまとめておくとしよう。
ファイル入出力機能を追加している。
*
大きなファイルの扱いについての対応がOSごとに違ったと思うので、その辺を共通のインターフェースで扱えないときつい。
linuxはfopen()と似たような感じでfopen64()とかの関数が用意されているが、windowsは全く別の名前の関数がwin32APIに用意されていた気がする。
*
fopen()などが、読み込み専用で開いたファイルに対して、失敗するとはいえfwrite()などの書き込み用関数を使える仕様なのが、私としては気に食わない。
なので、読み込み専用で開いたのなら書き込み用関数を呼び出せず、書き込み専用で開いたのなら読み込み用関数を呼び出せない、呼び出そうとしてもコンパイルエラーになるような構成にしようと思っている。
無論、読み書き可能で開いた場合には読み書きどちらの関数も呼び出せるようにする。
大体の構成はすでにイメージできているので、今日中にはlinux側の実装を片付けてしまいたいところ。
*
作業用の環境はlinuxなんだけど、今はvmware上で動いているため、まともにOpenGLとか使うならwindowsなんだよなぁ。
しかしwindows側は仮実装状態だらけで、すべて実装するのはなかなか手間だ。
私としては早いところ上位ライブラリの作成に移りたいのだけど、肝心のその構成はまだ定まっていない。
上位ライブラリは実際に動くプログラムが必要とする機能を追加していく形になるのだから、ライブラリより先にプログラムの方を進めるべきなんだろうか。
とりあえずファイル入出力をやっつけよう。話はそれからだ。
グラボのlinux用プロプライエタリドライバは基本的にゴミクズ
*
作者に悪いだなんて思わないむしろ迷惑被った私に謝れ。
高速な3D描画が行えたとしても、まず安定しなきゃ意味がないのだ。
linux用のプロプライエタリドライバがまともなことなんてほとんどなかった。
初めて自作したマシンにインストールした時、64bitOSなのにnvidiaのドライバときたらメインメモリが4GBより多く積まれていると起動に失敗した。
startxする段階だったかOSの起動の段階だったかは忘れたが、確実にフリーズするのだ。64bit用のドライバなのに64bitに対応していないらしかった。
そのせいで、マシンをまともに使うというためだけにatiのグラボを購入したことがある。
*
またある時nvidiaのグラボを使っていたら、マルチディスプレイでの利用時に奇妙なことに気がついた。
OpenGLでの3D描画を行うプログラムを作った時、サブディスプレイでそのプログラムを表示すると、CPUの1コアの使用率が100%になってしまうのだ。
垂直同期を有効にしているにも関わらず、メインディスプレイでは垂直同期が効くのにサブディスプレイでは垂直同期が効いていなかったのだ。
*
atiのドライバもだめだめだ。
最近というか今さっきまで起こっていたことだが、安定板のドライバを使っていると、ウェブブラウザのGPUアクセラレーションが使われるタイミングで、非常に高い確率でフリーズする。
そうでなくとも、dmesgを見るとドライバモジュールが頻繁にBUGという文字列を出力している。
ベータ版のドライバを使ってみたところウェブブラウザ程度で高確率でフリーズすることはなくなったが、flashやjavascriptでの描画が真っ黒になったりちらついたりということが頻繁に発生する。
フリーズも高確率ではなくなっただけで発生する。
これは確証はないのだが、ターミナルソフトのサーバプログラムにも影響を及ぼし、サーバプログラムが勝手に死んでしまうということも起きた。どうもフォントの描画処理らへんのライブラリとの相性がよくないように思える。
ターミナルのサーバプログラムが死ぬと、開いているターミナルは全ておじゃんになる。非常に迷惑だ。
*
で、結局、私はnvidiaやatiのドライバを使うのをやめた。今は仮想マシンをwindows上のvmwareで動かしている。
OpenGLは2.1ぐらいまでしか使えないようだが、特に不便はない。ターミナルの透過処理も全く問題なくできているし、動画再生を行なっても全く支障がない。
マルチスクリーンを全て使って、vmware上でない、直接起動した時と全く同じ表示にできるし。安定した環境というのはいいものだ。
やる気がなくソースが公開されてないので誰も修正できないソースから作られたものなど、所詮どうしようもないものでしかないということらしい。
*
vmwareのオープンソースドライバも全部が全部公開されてるのかしらんけども。
atiのグラボ用のオープンソースドライバとかもあったが、びみょうに残念だったな。画面の回転ができなかったのだ。
将来的には可能になるようなのだが、私はサブディスプレイを縦向きにしているため、回転できないと困る。いずれじゃ困るんだ。いるのは今だ。
gentooの32bit chroot環境の構築も完了した。
*
前の環境にはclangとか入れてたっけ。今は入れてないが、使う機会がまだないからいいだろう。
*
pulseaudioのpa_threaded_mainloopをpa_mainloopに置換できるかどうか試したが無理だった。
複数のpa_mainloopは生成できないらしい。しようとするとエラーが出て落ちる。
グローバル変数を使ったクソ実装の香りがする。
結局、動いているから今は触らない方がいい気がしてきた。
改良するなら、もはやpulseaudio以外を使うくらいしかない。
しかし、pulseaudioの作りが奇妙すぎていらいらする。
たかが音声を再生するために、
こんな感じ?pa_threaded_mainloopとかpa_mainloop_apiとかpa_contextとかってなんやねん。それって本当に必要か?
出力先デバイスの指定はpa_stream生成時に行なうのに、ロックの単位はpa_threaded_mainloopだし。まさかとは思うがデッドロックを回避するためとかほざくんだろうか。
まぁいいや。これ以上考えても頭が痛くなるだけだ。
*
音声出力オブジェクトをポーズ状態で生成する関数を追加したら、そろそろ次のステップに移ってもいい気がする。
しかし、イベントハンドラの差し替え機能とかいらないんじゃないかなぁなどと、今更になって考えている。
そんな機能が必要なら、ライブラリ利用側で勝手に用意すればいいのだ。
イベントハンドラの差し替えなんかしない場合、イベントハンドラ呼び出し時に毎回std::unique_lockかけるのなんて無駄だしなぁ。
差し替えないなら、イベントハンドラ取得関数なんてのも無駄になるし。省いてしまおう。
gentooの起動が問題なくなった。
*
うまくいかなかったのは、GPTなハードディスクについてのマウントポイント指定をUUID=で/etc/fstabに記述していたのがいけなかったのかな、という気がする。
blkidで出力した結果にはPARTUUID=と書いてあったのに、見慣れないからというだけで、多分大丈夫だろうと思いUUID=にしてしまったのだ。PARTUUID=にしたら問題なくなった。
気がついたら問題なく起動できるようになっていたため、それを直したら直ったのか、直した後もだめだったのかなんなのかはわからない。
少なくともカーネルモジュールの組み込みについては、修正する必要がなかった。
*
あと、UEFIモードで起動した場合に何も表示されない問題も解決した。
正確にはコンソールが出ないだけであり、Xは問題なく動作していたが。
UEFIモードは最初からフレームバッファが動作しているらしく、それに関係するモジュールを組み込んでおき、フレームバッファコンソールに対応していないといけなかった、ということらしい。
試行錯誤を繰り替えした結果、以下のモジュールをカーネルに組み込んでおけば問題ないことが分かった。
どちらかもしくは両方がなかったり、モジュールになっていたりすると、コンソールが出なくなる。initramfsで読み込んでもいいのかもしれないが未検証。
*
あとは、efibootmgrに地味に苦しめられた。
マザボ、というよりはUEFIに依存すると思うが、初期状態ではエントリの一覧表示ができなくて困った。
先頭のエントリしか表示されず、表示されないエントリについてブートオーダーを変更しようとしたり、エントリの削除などを行おうとしても失敗してしまう。
解決策は、一度エントリを追加し、追加したエントリを削除する。これで問題なくなった。
いや、最初からまともに動いてください。困るので。
efibootmgrでブートオーダーをいじくっていたからなのかはわからないが、UEFIからして起動しなくなってしまったりもした。原因は特定できていないが、CMOSクリアをかければ問題なく起動するようになる。厄介な。
ちなみにマザボはASRock 990FX Extreme4。
*
これでOSのインストールやら起動やらは問題なくなった気がする。しかしながら、gentooへのソフトのインストールが不完全だ。32bitプログラムの動作環境も整えていない。
multilibでなければならない64bitプログラムがなくなったので、nomultilibにしてしまったのだ。emulライブラリとか気に食わんのでいいんだけど、chroot環境の構築めんどいなぁ。
環境構築し直していた。
*
UEFIモードでOSをインストールし直した。
windows8はインストールが完了しているが、gentooの方は不完全。
windows8上で動作するVMwarePlayerでなら起動するが、単体では起動できていないっぽい。
ぽい、というのは、画面に何も出力されないからわからないのだ。
カーネルの設定がおかしいのか、なにかツールが足りないのか、起動後ずっと何も表示されない。
windows8上で動作するVMwarePlayerでも同じく何も表示されないのだが、GUIを使うつもりがないので一応問題ない。
sshで接続して作業したり、sambaの共有フォルダにアクセスする程度なので支障が出ないのだ。
しかし、起動にこけたりしたら原因を特定できないので、早くどうにかしないと。
表示が問題ないインストールディスクで使ってるものと、ほぼ同じ設定ファイルを使ってビルドしたカーネルでもだめだった。
ほぼ同じ、というだけで、initramfsが不要な形に修正しているのが原因かもしれない。
あるいは、initramfs内のスクリプトに答えがあるのかもしれない。
とにかくめんどうだ。UEFIモードにしたおかげで起動直後のポスト画面の表示時間が短縮され、起動が速くなったのはいいのだけど。
あと、UEFIモードにしたおかげで、gentooの起動にGRUBなどのブートローダがいらなくなった。UEFIスタブとかいうやつで、カーネル単体で起動できる。
これ以上時間食いたくないなぁ。作業に戻りたい。
だがしかしgentooの単体起動が。ぐぬぬ。
起動開始直後に、ハードディスクのアクセスランプが少し点滅した後すぐ反応なくなるところから考えると、/のマウントに失敗しているのだろう。
/のマウントに必要な機能をカーネルに含めれば、最終的にディスプレイマネージャが立ち上がってログインできるようになると思うのだけど。
インストールディスクで起動してlspciすれば解決する予感がするがめんどくさいのだ。
*
グラボを新しくするべきか考え中。
具体的には、グラボをultra fast boot対応のものにしようかなぁと考えている。
ultra fast bootにしたマシンを起動する動画を見たが、休止状態やスリープなどからでない通常の起動で、電源投入から5、6秒で操作可能な状態になるので、かなり速い。
しかし、現状でもwindows8のシステムドライブをSSDに変更したこともあり、起動速度は十分な気がする。
起動速度とわずかなパフォーマンス向上のために、1万払うのはなんだかなぁ、という気持ち。
*
gentooのUEFIモードでのインストールには、SystemRescueCdを使った。
公式のインストールディスクがUEFIモードでの起動に対応していないので仕方がない。
ていうか、SystemRescueCd自体がgentoo。公式のインストールディスクに毛が生えたようなもの。
startxって打てばX起動するし、GParted使ってGUIでパーティションいじくれるし。
UEFIモードでの起動に加えてgdiskやefibootmgrも入っているので、UEFIモードでのインストールも問題ない。
USBメモリも作成できるので、作っておくと便利。
ようやく環境が整ってきた感。
*
ホストOSをwindowsにできるようにしていた。簡単に言えばgentooとwindows8のデュアルブート環境にした。
しかし、ただデュアルブートにするだけではOS間のデータ共有が面倒だったりと、不便なことになってしまうので、少し工夫した。
ホストOSがwindowsの場合、vmwareplayerを使ってgentooを仮想マシンとして起動できるようにした。
起動するgentooはデュアルブートで起動できるものと全く同じ。仮想マシンのディスクに物理ディスクを割り当て、起動できるようにした。
sambaやsshが入っているので、samba経由でデータを共有したり、sshでログインして作業したりもできる。
windows側にはcygwinを入れてあるので、cygwinにXサーバを追加すればGUIプログラムも起動できたりするんだろうか。
まぁさすがに、linux用のdpをいじったりする場合には、gentooを直接起動しようと思うけど。
*
そんなわけで、windows用のdp開発環境が整ったわけだ。
ここ数日環境構築に費やしてしまったので、早く開発に戻りたいところ。
マウスも対応した。
*
vmwareにつっこんだgentooは、割といい感じに動いている。
open-vm-tools-kmodのインストールは、カーネルを3.9系のものにすることで対応した。
「複数のモニタを循環する」とかいう、何言ってんのか分からない機能を使うことで、複数のモニタにフルスクリーン表示することができていい感じ。
OpenGLも使えたが、想定通りというか、おまけっぽい印象を受けた。とりあえず、深度バッファが機能していないっぽかった。あるいは、プログラムの方がいけないのかもしれない。深度バッファのサイズ指定してなかったかも。
そうでなくても、OpenGL2.1ぐらいまでしか対応しないようなので、期待はできない。
とりあえずブラウザや動画プレイヤーをインストールしてみて、どんな感じで動作するのか確認する。
vmware上でgentooを動かしているマシンはCPUがintel-vtに対応してないcore2quadなので、amd-vに対応したメインマシンではどうなることやら。性能上がるといいんだけど。
ああ、あとグラボもよくなる。メーカーからして違うが、げふぉのローエンドかららでおんのミドルエンドになる。
intel-vtに対応していないせいで、32bit版のgentooを入れるはめになったが、メインマシンには64bit版を入れる予定。
*
32bitのマシンでdpをビルドして分かったことは、OpenGLの定数をenumで定義するのはびみょうだということだ。unsigned long longな値とかあるせいで、コンパイルエラーになる。
enumではなくconst autoにしたら通ったので、後で直しておこう。
とりあえずキーボード対応した。
*
windows側、うまくできるか不安。
*
そんなことより、仮想マシンにつっこんでるwindowsがまれによくホスト巻き込んでフリーズして参ったので、ホストをwindowsにしようかと思っている。
まず、すでにwindowsが入っているマシンにvmwareを入れて、その上にgentooをインストールして色々試してみることにする。
しかし、VMWarePlayer、バージョン5かららしいが、バルーンヒントが消せない。
「お使いのコンピュータに戻るには、Ctrl+Altを押してください」とかいうのが毎回出てきてうざい。
それが出てるところクリックしても、その下をクリックしたことにならないし邪魔なことこの上ない。
前のバージョンを使えるようなら変えてしまおうかしら。
*
あと、vmware toolsのインストールがうまくいかない。
gentooのリポジトリにopen-vm-toolsとかいうのがあるので、それをインストールしようとしているのだが、open-vm-tools-kmodのインストールでこける。
どうやら、新しいカーネルに対応していない記述になっているらしい。
ちょっと検索してみたら対応するためのパッチが見つかったので使おうとしたが、どうも古いバージョンのvmware tools用らしくパッチが失敗する。
すごいめんどくさい。しかしやらねば。どう対応しよう。古いカーネルのソースからカーネルを構築しようかしら。それが一番楽そう。
細かい修正をしていた。
*
そう細かくないこともしてたけど。インターフェース変えたり。
ウィンドウやディスプレイの幅、高さ、位置などのデータをdp::Long(long long型)やdp::ULong(unsigned long long型)にしてたのをやめた。dp::Int(int型)で統一した。
理由としては、幅や高さをOpenGLで使用する場合に不便だから。それに、幅や高さの指定に64bit整数が必要になることなんて、絶対にないなんてことはないだろうけど、まぁすぐには必要にならんだろう。
パソコンのディスプレイの解像度なんか、1920x1080とかいうクソみたいなのが主流のようだし。横長やめろよテレビじゃねぇんだぞ。
*
あと、linuxの場合のみだが、dp_commonのロード時、アンロード時に行なっていたXOpenDisplay()やXCloseDisplay()の呼び出しを、別のライブラリ(dp_xlib)に分離した。
dp_windowやdp_displayなどで必要になるので、共通的なdp_commonのロード時、アンロード時に処理させてたんだけど、dp_commonはdpを使用するプログラムに必須。コマンドライン引数の処理からしてdp_commonの関数を使ってるし。
なので、dp_windowやdp_displayなどを使わないプログラムであっても、XOpenDisplay()やXCloseDisplay()が呼び出されることになってしまう。そんな無駄なことはしたくないので、別のライブラリに分離して、それをdp_windowやdp_displayなどから参照する、という形にした。
*
そんな感じか。この次はキーボードとマウスの対応を行なう。
キーボードの対応がうまくできる気がしなかったので今まで敬遠してたんだけど、風呂入ってる時にいいアイデアが浮かんだので試すことにする。起きたら。
*
そういえばpulseaudioの利用周りを修正するみたいなこと言ってたけど、想像以上に無茶そうなのでやめた。
オブジェクトの破棄時にもロックする必要があるとか困る。std::unique_ptrで対応できないじゃないか。
ああ、あるいはpa_threaded_mainloopではなくpa_mainloopにすればよかったんかな。キーボードとマウスを対応したら試してみるか。
はー、できたできた。
*
途中、エルミナージュとかやってたけど、ひとまず音声出力を使えるようにした。
dp::AudioPlayerを生成すると再生が開始し、dp::pause()で一時停止ができ、破棄すれば再生が中断され、再生が完了すればイベントハンドラで通知される。
出力データの書き込みは、必要になったらイベントハンドラでバッファ領域と共に通知される形なので、専用のスレッドを使ってループの中でどうのとか、めんどくさい処理を書く必要はない。そのような処理はライブラリ側に書かれている。
*
dp::pause()で一時停止はできるけど、現在一時停止中か再生中か、というデータを得られる関数がないんだよなぁ。
ライブラリ側から勝手に一時停止したりすることはないんだし、その辺はライブラリ利用側で必要なら作ってもらう形でもいいかなと思っているんだけど。
基本的に機能は最小にしたいと考えている。環境間の差異を無くすライブラリなわけだし。
*
しかし、pulseaudioの使い方がこれでいいのかよくわからん。
pa_threaded_mainloopは全体に1つだけ、デバイス接続・切断管理オブジェクト1つにpa_context1つ、音声出力オブジェクトは全て共通でpa_context1つ、のような形にして、できるだけ生成する量を少なくしているんだけど、ロックはpa_threaded_mainloopで行なうんだよなぁ。
dpdemosに作ったプログラムはデバイス接続・切断管理1つ、音声出力1つ程度なので問題ないようだけど、音声出力を大量に作るような場合影響が出るような気がする。
デバイス接続・切断管理オブジェクトや、音声出力オブジェクト1つ1つに、pa_threaded_mainloopを持たせるべきなんだろうか。
でも、音声出力すんぞって時に、色々作るとそのオーバーヘッドが気になってしまう。
いかんせん、サンプルやドキュメントがなさすぎて分からん。試しに作り変えてみようかな。
想定では、現状よりも分かりやすくなるはずなんだけど。現状はライブラリロード時にpa_contextの接続関数を呼んだりしてるけど、非同期な関数なもんで、処理が戻ってくる=接続完了しているにならんのでめんどい。
一時停止を用意するべきだなと思った。
*
音声出力は普通、書き込んだ量と出力した量は一致しない。
書き込みはバッファに行なわれ、出力はバッファから行なわれる。書き込んだ量>出力した量となるのが普通だ。
想定される操作として、一旦出力を停止し、その後停止したところから出力を再開する、というような操作があるが、これを実装するのは容易ではない。
いくら書き込んだか、という情報は簡単に手に入るが、いくら出力したのか、という情報は簡単には手に入らない。
書き込んだ量を元にして出力を再開すると、書き込んだ量と出力した量の差だけ音が抜けてしまうのだ。
これはよくないなぁと思い、どう対応したものか色々考えていたのだが、どうやら音声ライブラリには出力の一時停止というものがあるらしい。
一時停止すると、バッファからの出力が止まる。バッファに空きがあるなら以降も書き込みは可能だが、出力はされない。
一時停止を解除すると、バッファに蓄積されたデータの出力が再開する、というものだ。
pulseaudioにこういう機能があることは知っていたのだが、他のライブラリにも同等の機能が用意されているのか不安だったので敬遠していた。
しかし調べたところ、alsaやらwasapiやらxaudio2やらには、同等の機能が用意されているようだった。
directsound?なんか複雑でなぁ。よく分からん。あるっぽいんだけど確証がない、というか使う気がない。
*
ライブラリに用意されている機能を使っても、一度閉じたストリームの出力を再開するような動作には使えない。
そういうのはもう、再生と同時に経過時間を計測して、その時間を元に再開地点を算出すればいいんじゃないかなとか。
やる気減退してた。
*
昨日ようやく、wavファイルの解析処理を作ったところ。
それで今、音声出力のインターフェースを決めようとしてるんだけど、やはり難しい。
単純な形にしたいが、そうするとどうしても綻びが出てしまう。
やはり、古いプロジェクトで作ったようなインターフェースにするべきなのだろうか。
*
単純に、オープンして、書き込んで、クローズする、という仕組みにしようと思っていたんだけどなぁ。
ファイルの書き込みによく似ているから分かりやすいが、ファイルへの出力と音声の出力はやはり違うのだ。
ファイルへの出力というのは、基本的にデータを全て出力するものであり、書き込みが成功したか失敗したかに関心が向く。
音声の出力はそうではない。全て出力した場合には最後まできちんと音が鳴ることは確かに重要だが、途中で出力を中断するケースもある。
その場合に直ちに音が鳴り止むべきであるし、そんなことよりも出力している最中に出力を途切れさせて、音飛びが発生しないようにする必要もある。
あと、ファイルへの出力の場合、普通出力するデータは出力する前に全て作成しておくべきだが、音声の出力はデータを順次作成していく形にするべきだ。
音声データは基本的にサイズが大きくなりがちだし、途中で中断する場合も考えれば中断した以降のデータは無駄になる。
他にも、例えばマイクから拾ってくる音声のように、終わりがないものだってある。そういうデータは取得したデータを順次出力していくしかない。
*
ファイルの場合もそうだろうけど、1度に出力できる、バッファのサイズは決まっている。
ファイルの場合と違い、音声はバッファに出力したデータを全て再生しきる前に、次のデータを出力しなければならない。ファイル出力なら特に問題はないだろうけど、音声出力の場合は音飛びになってしまう。
そのためにはバッファの空きサイズなんかは重要なデータだ。しかし、それは時間経過で変化する。例えばgetEmptyBufferSize()みたいな関数を用意して、現時点でのバッファの空きサイズを取得する、なんてのも考えられるだろうけどスマートでないと思う。
取得した次の瞬間には増えているかもしれないし、なにより出力時以外に使用タイミングは無いようなものなのだ。出力時にのみ得られればいいデータを、いつでも得られるようにするのは不自然だ。
*
とかなんとか、色々考慮した結果、古いプロジェクトと同じように、イベントハンドラで出力データをセットする、という形にした方が良さそうだ。
インスタンス生成でストリームオープン、イベントハンドラ内でバッファにデータをセットし、インスタンス破棄で再生を中断しストリームクローズ。再生終了についても、イベントハンドラで通知する。
というような感じだろうか。古いプロジェクトのと違う点は、再生終了時のイベントハンドラが無かったのと、インスタンス破棄時の処理だろうか。前は破棄で再生終了の待機をしていたはずだ。
alsaにはデバイスの接続・切断検知機能がないことを忘れていた。
*
udevを使ってデバイスの接続・切断及び列挙を行うと、実際には使えないと思われるデバイスまで列挙されてしまうことも忘れていた。
やはり、pulseaudioでやった方がいい気がしてきた。alsaはストリームが外部に見える仕組みとして存在しているわけではないが、結局音を重ねる場合には複数のストリームを開く必要があるわけだし。
pavucontrolのことなんぞ考えず、pulseaudioでやるべきか。
どの内部実装を使ったとしても、それなりに複雑な仕組みを構築する必要があることに変わりはないわけだしな。
それならば、比較的扱いやすい方を選んだ方が得策だ、多分。
なにより、どうせlinux側の実装なんぞ私ぐらいしか使わんのだ。やりたいようにやらせてもらう。
pulseaudioは多分だめだ。
*
1つ音を重ねようとするたびに別のストリームを作らなければならない作りが納得いかない。
内部的にはそうする必要があっても、外部に見える仕組みとしてそうなっているのはどうなんだ?
pavucontrolで外部から音量調整しようとすると、ストリームの数だけ音量調節バーが出現するわけで、ゲームに使おうとするとどうにもうまく運用できる気がしない。
というわけで、jackの方を試してみることにする。
*
試そうと思ったがやめようと思った。
どうもjackは、複数のサウンドカードを同時に扱えないらしい。
やはり、alsaを直接叩いた方がいい気がする。
OpenGL対応した。
*
公式にあったヘッダファイルから変換してなんとかした。
しかし、OpenGL1.0と1.1の定数とか、関数の一部が記述されていなかったため、その部分は過去のコミットから引っぱってきて完了した。
確認できているのはその部分、というだけであり、もしかしたら以降のバージョンでも不足があるかもしれない。めんどいので、その場合には気付いた時に修正する。
*
これで後は音だけだろうか。もちろんwindows側がほぼ空っぽなので、その辺は後でなんとかしなくてはならないが。
OpenGL1.2を対応した。
*
しかし、拡張機能の対応を忘れていた。1.2から出てくるようだ。GL_EXTENSIONSの値をglGetString()で取得し、値の中に特定の文字列が含まれているならその拡張に対応している、とかいうあれ。
ここまで書いて仕様書に目を通していたら、どうも1.1の時点であったっぽいな。dpではとりあえず全てのOpenGL関数をロードして利用可能にするというだけにする予定なので、関数のバージョンや拡張機能の括りを考えたりはしないので問題ない。
しかしながら、上位ライブラリではバージョンや拡張機能単位でOpenGL関数をロードする仕組みを用意するつもりなので、どこかにその辺はっきりした資料があるといいのだけれど。
ってあれ。公式サイトを色々探してたら、glcorearb.hとかいうのが使えそうじゃないか。あとglext.h。
前者にはバージョンと拡張機能別の定義、後者にはバージョンに存在する可能性のある拡張機能を含んだバージョン別の定義が書かれている。
これだけでもいけそうかなと思ったが、びみょうにだめっぽい。後者のヘッダファイルに書かれているけど前者のヘッダファイルに書かれていない関数とかある。その辺は仕様書読まないと分からんなぁ。
しかし、ベースはglext.hでいいような気がする。
もう仕様書を読みながら関数の定義を記述しなくていいんやな。
OpenGL1.0のから書き直した方がいい気がしてきた。
*
拡張機能は、ARBとかいうのとそれ以外ので大別できそうな感じがするな。前者はバージョンに深く結びついていそう。
ARBのはバージョンに対応したヘッダファイルに記述してもよさそうだけど、それ以外はどうしよう。できるだけファイルを分けて、ファイル1つ辺りの行数を減らしたいしなぁ。
あまり分割しすぎてもあれだけど、行数が多すぎるとリドゥやアンドゥに時間がかかりすぎて困る。
しかし、定数と関数で別ファイルにする、2ファイル構成でもいい気がする。ううむ。
昨日、OpenGL1.1まで対応した。
*
OpenGL1.0の関数で描画とかしてみたんだけど、やはりバージョン毎のロード処理は必要な気がする。
使う関数を全てdp::loadGLProc()するのはめんどくさい。
ロード対象の関数ポインタを配列にまとめ、for eachでロードするというのを想定していたが、参照を配列にできないため、ポインタの配列にせざるを得ない。
なので、関数ポインタのポインタを取得するような関数があった方がいいなぁ。
*
そんなことより、垂直同期するための関数、glXSwapInterval_EXTとか、ライブラリのロード時に取得し、ラムダ式をグローバルに設定する、ということをしてるんだけど、いざその関数を呼び出そうとすると、なぜかぬるぽになっていて非常に困る。
ロード時には確かに設定しているし、ロード時以外では代入もしていないのだけれど。一体どこでおかしくなっているのかさっぱりだ。
ってなんだよ今動かしたらまともに動いてるじゃねーかくそどういうことだよちくしょう。クリーンしてなかったからいけなかったんか。
*
バージョン毎の関数は、std::vectorをライブラリ側に持たせるとかいう感じにするかなぁ。今はマクロによる関数の定義をバージョン毎にヘッダファイルを分けて記述しているが、std::vectorをバージョン毎に用意するようにすれば、マクロの方は1つのファイルにまとめてもいいかもしれないなぁ。
なんだよもー
*
結局、単純作業などいらなかった。やる前なので助かった。
昨日うまくいかなかったのは、関数ポインタの参照が問題だったようだ。関数ポインタの参照に関数の実体をつっこんでしまって変な値が出てきてしまったらしい。
そんなわけで、glClear()を直接関数ポインタにつっこむのも、glXGetProcAddress()で取得したものをつっこむのも、どちらでもうまくいったわけだ。
私としては後者で統一したいが、windowsじゃうまくいかない可能性があるし。前にやったのが失敗とかならいいのだけれど。
*
まーとりあえず、OpenGLの関数取り込みに関連する処理をしっかり固めよう。1.0より後のバージョンの対応はそれが済んでからだ。
あるいは、現時点では関数1つ単位でしかロードができないので、バージョン単位でのロード処理も作るべきか。しかし、それは上位ライブラリでやってもいい気がする。
OpenGL1.0対応、完了は見えてきたのだが。
*
単純作業は終わったと思ったのだが、想定外の事態によりまだ単純作業が必要になりそうだ。
glXGetProcAddress()で、OpenGL1.0の関数のアドレスも戻ってくるんで、これを使えると思っていたのだが使えなかった。
関数の呼び出し自体は問題なく成功し、処理も帰ってくるのだが、どうも何も処理していないようだ。
glClearColor()の設定がされないし、glGetString()でGL_VERSIONやGL_VENDORの文字列を取得しようとしてもぬるぽが帰ってくるだけ。
どうもダミーっぽい香りがする。
別に、これについては想定の範囲内だ。windowsではopengl32.hに記述されている関数は、wglGetProcAddress()で関数のアドレスを取得しようとしてもぬるぽが帰ってきてた気がするし。
仕方ないので、glXGetProcAddress()の戻り値を設定するはずだった関数ポインタに、関数の実体のアドレスを設定するようにしたのだが、これもうまくいかないのは想定外だった。
どうも、同じ関数であっても、ライブラリ上から呼び出す場合のアドレスと、ライブラリ上からではない、プログラム上から呼び出す場合のアドレスは違うらしい。
なんか分かりにくいな。libGL.soにある関数を呼び出す場合に、libdp_opengl.soから呼び出す場合と、main()のあるソースから呼び出す場合のアドレスが異なるので、libdp_opengl.soから呼び出す場合のアドレスをmain()から使って呼び出そうとしてもうまくいかない、というわけだ。セグメント例外になってしまう。
これを解決するためには、main()のあるソースから呼び出す場合のアドレスをなんとかして取得するか、もしくはlibdp_opengl.soから呼び出す場合のアドレスを使い、libdp_opengl.soの関数を経由して呼び出すかのどちらかの方法になるだろう。
しかしながら、前者の方法はまるで見当がつかない。なので、後者の方法、libdp_opengl.soを経由する方法になるのだが、これはつまりlibdp_opengl.soにlibGL.soにある関数を呼び出すだけの関数を作る必要がある、というわけであり。OpenGL1.0に対応するなら、OpenGL1.0にある関数の数だけ作る必要があるわけであり。
すごいめんどくさい。ていうか、libdp_opengl.soの関数のアドレスをmain()に渡しても呼び出せるのかなぁ。まずそこの検証からか。うまくいかんかったらどうしよう。困るなぁ。
OpenGL1.0対応を始めている。
*
予想はしていたが、やはりめんどくさい作業になりそうだ。
どのバージョンでどの関数や定数が追加されたとかいう情報が、公式から配布されている仕様書のPDFしかないっぽいもんで、それを見ながらヘッダファイルを作っている。
最終的には、関数や定数の定義は1つにまとめて、各バージョン毎に関数のリストを作る予定。なので、定数までバージョンごとにまとめるのは無駄かもしれない。
しかしながら、最新バージョンでは非推奨になってる関数とかあるわけだし、その辺の記述がもしかしたら最新バージョンの仕様書からは抜けてるかもしれない、知らんけど。なので、1つずつ仕様書を追っていこうかなーと。
直したと思ったら、修正前のソースでも問題が再現しなくなったでござるの巻
*
少なくとも、常にウィンドウ内全てが再描画されるというのは問題なかったようだ。
てっきり、自動的にクリッピングがかかって再描画が必要な部分にのみ描画結果が適用されるものとばかり思っていたんだけど。そんなリッチな機能が最初から有効になっているはずがなかったらしい。
で、初回の描画が表示に反映されない問題についても、何回やり直しても問題が再現しなくなってしまった。ぐぬぬ。
修正したソースは残しておくけど、とりあえずは修正前のままでいいか。
*
とまぁそんな感じで時間を無駄にしてしまったわけだが。
なんかやる気減退してしまったので起きてからになるかもしれないが、OpenGL関数の扱いに入るとしよう。
目指すところとしては、まずライブラリを使うプログラムが、OpenGLのライブラリ(libGL.soやらopengl32.dllやら)に直接リンクしなくてもいいということ。古いプロジェクトではwindows版のみ、opengl32.dllにリンクしなくてはならないところが気にかかっていた。環境によってはリンクしなければならない外部ライブラリがあるというのはいやだ。
とりあえずはそれだけか。あとは、バージョン1.1より後の関数はglXGetProcAddress()やらwglGetProcAddress()やらを使い、文字列を引数に関数のアドレスを取得しなければならないが、関数名をプログラム側に含めさせたくないとか。そもそもそのアドレス取得の環境依存関数はプログラム側に使わせたくないな。
ひとまず、OpenGLの関数は、何もしなくても使えるバージョン1.1以前の関数を含め、全て関数ポインタとして提供し、何もしなければ関数ポインタの値はnullでいいかなと思っている。
そこに、なんらかのロード機構を用意し、プログラムで使用する関数のみ、関数ポインタが適切な値になるようにする、といった感じで。
もちろん、環境が対応してなければ関数ポインタはnullのまま。それを利用して、プログラム側で判定を行ない描画処理に変化を持たせたりとか、後々作るシステムで、指定した関数群のポインタがnullのままだったらゲームを起動できないようにしたいなとか。
引数に何を使うかが問題だ。できることなら、関数ポインタそのものを引数にしたいのだけれど、配列にできるだろうか。ほとんどの関数、型が異なるわけだしなぁ。voidのポインタとして扱えば可能か?
なんにせよ、量が多いのが気が滅入る。まずはロードの必要がない関数についての処理を実装してしまって、次にロードが必要な関数についての処理を、ん、いや、逆がいいのか?後者の方が複雑そうだし、うーむ
OpenGLコンテキストできた。
*
コンテキスト生成とウィンドウとの関連付けを別々にしたし、垂直同期するかしないかの指定も適切な場所で行なうようにした。
垂直同期に使用する関数も、ファンクタを使って呼び出し時にはif文が不要になるようにした。
古いプロジェクトのよりずっとよくなっていると思う。
しかし、びみょうに想定した動作をしていない。
初回の描画が表示に反映されてない場合があったり、再描画時に無効領域以外も再描画してしまう、つまり常にウィンドウ内全てを再描画してたり。
ウィンドウの描画イベント周りがだめなんだろうなぁ、ということがわかる。
直し方のイメージはできている。
かなり大幅に修正を加える必要があるが、はっきりイメージできているため途中で困ることもないだろう。
だが疲れた。
明日にさくっと終わらせて、OpenGL関数の扱いに入りたいところ。
新しいデモ作るのめんどいなぁ
*
描画関係のイベントハンドラ対応終わらせて、再描画要求も追加した。
で、マルチスレッド関係でまずいところがあったのを修正して、他にも数点修正した。
これでOpenGLに入れる。のだが、まだ新しいデモ作ってない。
後でもいいんじゃないかなーとか思い始めている。
だって私が特に得しないし
*
そんなわけでOpenGLを始めよう。まずはコンテキストからだな。
古いプロジェクトで作ったやつだと、生成処理でコンテキスト生成とウィンドウへの関連付けを一緒にやってて、破棄で関連付け解除とコンテキスト破棄を一緒にやってる形になってるんだけど、コンテキストの生成・破棄とウィンドウへの関連付けは別々にした方がいい気がする。
コンテキストの生成・破棄は、ウィンドウが存在していようがいまいができるからなぁ。先に生成しておいて、ウィンドウを生成してそれに関連付ける、とかそういう風にできてもいいはずだ。
やったことがないから分からないけど、他のウィンドウに関連付けたコンテキストを、関連付け解除して他のウィンドウに関連付ける、とかもできるんじゃないかなぁ、多分。
昨日書いた残件に、ウィンドウクローズ要求書いてなかったな。
*
でもまぁ、要求系は全部終わらせたわけだ。あとは描画関係のイベントハンドラ対応を終わらせればいいだけになった。
しかし、動作確認用に作っているデモが、でかくなりすぎている気がする。
現時点で641行ある。行数だけならまだしも、ファンクタとか使いまくっていて、少々ではないレベルで読みにくい気がするんだよなぁ。
今は、大体1ライブラリにつき1デモという感じになっているんだけど、もっと細かく分けるべきかもしれない。
デモのソースを見れば、使い方が分かるような感じのを目指しているので。量や、変に複雑そうな処理で読みにくくなるのはいただけない。
*
まーとりあえず描画関係のイベントハンドラ対応を終わらせよう。今あるデモにその動作確認のコードも追加するが、その後にそれとは別のデモを作ることにしよう。
ウィンドウを動かした時と、サイズ変更した時のイベントハンドラ設定、呼び出し処理を追加した。
*
数日、セブンスドラゴンばかりやっててほぼ進んでいなかった。おもしろいがよろしくないので自重することにした。数日というのは土日も含まれるが、まぁ関係ない。
で、ウィンドウの残件は以下のような感じだと思っている。
*
描画関係のイベントハンドラ対応(描画開始、描画、描画終了)
ウィンドウタイトル変更要求
ウィンドウ移動要求
ウィンドウリサイズ要求
*
こんなところか。
フルスクリーンはやはり後回し。まだ描画すらできていないし、そんな状況でフルスクリーン対応しても楽しくないしなぁという意見。
ウィンドウを常に最前面だとか、リサイズ不可だとか、その辺の属性?も変更できるようにするべきなのかなぁと思わないではないのだが、それさえなければ普通のウィンドウとフルスクリーンウィンドウを同じように扱えるような気がしているので、実装しない方がいいかなぁと。
というか、そんなんウィンドウ作り直せばいいじゃんと思う。
*
早いところ片付けて、OpenGL対応してなんか描画したい。
しかし、OpenGL対応も色々考えんとなぁ。
過去に作ったプロジェクトのだと、とりあえず関数呼び出せるようにすりゃいいだろという感じでやっていたが、実際には環境によって、ある関数が使えないとか使えるとかあるわけで。
ゲームごとに、起動にはこの関数群が必要でそれらが使える環境じゃないとゲームを起動させることができないだとか、この関数群が使えなくてもゲームをプレイできるが、演出が地味になるかもよだとか、そういうの設定できた方がよさそう。
過去に作ったやつは、OpenGLのバージョンごとにヘッダファイル分けてたけど、今回はどうしよう。結局のところバージョンなんてのは、そのバージョンで実装するべきAPIを全て実装してあればバージョン準拠を名乗れるだとかそんな感じなわけだし。
しかし実際、ハードウェアはそういった括りで作られているはずなので「OpenGLのバージョンX.Xに対応してれば起動できるよ」とかいう表示の方が、起動に必要な関数名を列挙するよりずっと分かりやすそう。あるいは、詳細な情報として列挙を見られるようにするというのもありか。
なんにせよ、早いところウィンドウ対応を終わらせよう。
iconvとかいうクソAPIにしてやられた。
*
iconv_t、マルチスレッド非対応なのな。マルチスレッドで使ったら変になった。
iconvで変換したものをウィンドウのタイトルに設定してたんで、最初ウィンドウのタイトルを設定する関数の方を疑ってしまった。
マルチスレッド時におかしくなるというのはいやだなぁ。原因が分かりにくい。
*
enumの値の論理和を取るとセグメント例外で落ちるのはなんでだろう。
結局はただの数値なわけだし、そんなんで落ちるんならコンパイル時にエラー出してほしいんだけど。値をintにキャストした上で論理和して、その結果をenumにキャストしたら問題なく通った。わけわかんね。
わけわかんねかったけど、とりあえずこれでリサイズ不可且つ常に最前面なウィンドウとか作れるようになった。よかったよかった。
次は描画やらサイズ変更やら移動やらのタイミングでのイベントハンドラ呼び出し処理を追加するべきか。
装飾なしウィンドウとかいらないと思った。
*
gtk+に、gtk_window_set_decorated()とかいういかにもな感じの関数があったんで使ってみたがウィンドウの装飾が外れない。
ぐぐりまくって、GTK_WINDOW_POPUPとかいうので作ったウィンドウが装飾なしだったんだけど、これどう見てもoverride_redirect=Trueなんだよなぁ。移動できないし、タスクバーに表示されない辺りウィンドウマネージャの管理から外れてる。
結局、audaciousのプレイヤーウィンドウがどうやって枠なしのウィンドウ作ってるのかとか、よく分からんかった。しかしながら、gkrellmも枠なしだけど、こっちは非矩形ウィンドウなので、もしかしたらaudaciousのプレイヤーウィンドウも、四角い形してるだけの非矩形ウィンドウなんじゃないかな、とは思った。
で、正直言ってフルスクリーンじゃない装飾なしウィンドウとか私がお呼びでないのでそんなもんサポートしなくていいかという感想。フルスクリーン用なら問題なく作れるし。
その代わり、そのうち非矩形ウィンドウをサポートするのはいいかもしれない。SDLの新しいバージョンでサポートしていたような気がするし、きっと環境非依存な感じで書けるんだろう。分からんけど。
*
今日は大体の時間これをやってた。twitterで流れてきたので。
最終的に所要時間0.1秒切れたので満足した。
競技プログラミングとかそういう系は基本的に性に合わないので、多分これっきり。
gtk+をいじくる。
*
リサイズ不可ウィンドウとか、常に最前面なウィンドウとかは作れるようにしたんだけど、タイトルバーやら枠やらの装飾なしウィンドウがどうしても作れぬ。
audaciousのプレイヤーウィンドウは装飾ないんだし、できなくはないはずなんだけど、ぐぐって出てきた情報を元にしてもうまくいかない。
で、audaciousはgtk+を使っているようなので、ならばgtk+を使って簡単なプログラム作った方が仕組みを理解しやすいんじゃ、となった。
audaciousのソースも見たんだけど、いまいちどこで装飾なしにしてるのか分からなかった。
スキンもプラグインとして実装してるっぽいからなぁ。メインのソースにGUIに関する記述が見当たらなかった。プラグインのソースにはあったんだけど、プラグインがどういう仕組みになってるのか知らんし、把握するの大変そうだ。
*
古いプロジェクトをやってた時にも書いたような気がするんだけど、フルスクリーンは別枠にした方がいい気がしてきた。
装飾なしで、リサイズ不可、常に最前面で、サイズがディスプレイ全体を覆うものをフルスクリーンウィンドウとしようとしてるんだけど、装飾なし、リサイズ不可、常に最前面、というのを、普通のウィンドウを作るのと同じように、3つのフラグを設定して、とかするのはなんか違う気がする。
linuxの場合とか、その3つのフラグの効果を合わせ持ったウィンドウよりも、より適したウィンドウがあるわけだし。そのような特性を持ったウィンドウが生成されることを期待するが、ウィンドウの生成方法は違う方法を取る、みたいな?なんかそんな感じ。
*
この言いたいこと伝わってない感
あ、セブンスドラゴンたのしい
*
っていうのを14:30頃に書いて、アップロードすんの忘れてた
昨日書いたように作り変えた、っていうのを書こうと思ってたんだけどすっかり忘れてた。
*
ウィンドウタイトルの設定とかまだしてないのでそれを終わらせたら、次はウィンドウをリサイズ不可にしたりウィンドウの装飾無くしたりとかその辺かな。
最終的にはマウスやらキーボードやらも対応するべきだと思うんだけど、今はまだいいかなという気分。
マウスはまだしも、キーボードはlinuxとwindowsで違いがありすぎてめんどいしなぁ。前に作った時も、後回しにして結局作っていなかったはずだ。
*
マウスの調子がおかしい。
前に使っていたマウスが、右クリックだったかホイールクリックだったかがおかしくなって、1回押しただけなのに何回も押されたような挙動になってしまったので、一時的にタブレットのマウスを使っているんだけど、もうだめになったらしい。
長いこと使ってなかったのが原因なんだろうか。右クリックの反応が鈍くなっているようで、押したのに押されてないことになったり、離してないのに離したことになってたり。
というのが最近発生していた問題なんだけど、今日それに加えて左クリックもおかしくなってきた。押しても反応しない時がある。
これは新しいマウスを買わなければなるまい。明日辺り探しに行こうかな。
とりあえずウィンドウ出した。
*
しかし、dp::Windowの破棄時にウィンドウが閉じるまで待機という仕様は、やはりびみょうな気がしてきた。
今考えている仕様だと、閉じるボタンを押した時に呼び出されるイベントハンドラでtrueを返すとウィンドウが閉じて破棄され、falseを返すとウィンドウが閉じないという感じにしようとしていて、これを利用して、いわゆる「本当に終了しますか?」みたいなことができるようにしているわけだ。
なので、なんらかの方法で、有無を言わさずウィンドウを閉じて破棄する方法が必要になるわけだけど、それはdp::Windowの破棄でやってしまえばいいんじゃないかな、と思った。
前に作っていたやつだと、閉じるボタンを押した時に呼び出されるイベントハンドラの戻り値がなく、閉じる前になんかできる、というだけだったから、破棄のタイミングで待機でもよかったんだけど。
22日の日記に書いたような、複数のスレッドで待機とかする気は全くないんだけど、待機はstd::condition_variableを使って自前でやる方法でいいかなーと。
ん、閉じるボタンを押した時に呼び出されるイベントハンドラでtrueを返すと、とかもいらん気がしてきた。そのイベントハンドラで待機を終了して、処理が再開し、ウィンドウを破棄されるので、閉じるボタンが押され、イベントハンドラを呼び出した後は、ライブラリ側ではなんもしなくていい気がする。
閉じるボタンを押しても、押したことが通知されるだけで、それ以上のことはやらない感じ。それでいい気がしてきた。
起きたらその辺整えよう。最近なんだか疲れている気がする。夜更かししすぎかしら。
ルミネスたのしい
*
dpの進みはわずか。ルミネスやりすぎた。明日から本気出す。
構成考え中。
*
試しに書いて色々考えた結果、挙動は前の前に作ったライブラリと同じような感じでいいかなと思った。
破棄関数で、ウィンドウが閉じるのを待機する感じ。
破棄関数では強制的にウィンドウを閉じ、待機は別に関数を作った方がいいかなとも思ったんだけど、複数のスレッドで待機できるようになる以上の利点を見出せなかった。
複数のスレッドで待機するとなると、待機しようとする前にオブジェクトが既に破棄されてないかも気にかけないといけないし、そもそも複数のスレッドで待機できるからどうなのだというのもあった。
なので変に変えず、前に作ったのと同じでいいかーと。
生成時の引数は変えることにした。前まではdp::Windowの生成にdp::WindowInfoの参照を渡し、イベントハンドラやウィンドウタイトル、幅、高さなどの情報を全てdp::WindowInfoに入れていたが、dp::WindowInfoはイベントハンドラだけ持たせることにする。
dp::WindowInfoはdp::Window内にそのまま持たせ、後々参照を取得し、変更できるようにするのだが、その時にタイトルやら幅やら高さやらが変更可能になっているとどうにも妙だと感じたからだ。
変更したからといって実際のウィンドウにそれが反映されるわけでもない。なので、その辺のデータはdp::Windowの生成時にdp::WindowInfoの参照とは別に渡す形にすることにした。
今まで、dp::なんたらInfoとかいう、なんとも曖昧な名前な構造体をいくつも作ってしまったが、dp::なんたらEventHandlersの方がしっくりくるかもしれないな。
*
ウィンドウをリサイズ不可にするとか、装飾を無くすだとか、その辺をどう実装するのかも大体まとまってきた。これなら全部書けるかもしれぬ。
リネームおわった。
*
次はウィンドウ生成に進みたいところだけど、やるのを渋っていた理由にlinux側のAPIが、新しいのが主流になるかもしれん、というのもあったんだった。
waylandかmirか。普通に考えれば前者が主流になりそうな気がするけど、どうなんだろう。
どちらにせよ、X11のAPIでも使えるようにするラッパーをどちらも用意しているようなので、とりあえずはX11ので作っちゃえばいいかなぁ。新しいのを使ってみたい気もするのだけれど、どんななのか知らんし、この環境にうまくインストールできるかも分からんし。
*
gitのbareリポジトリ便利ね。
今まではbitbucketのリポジトリをクローンしてきて使ってたんだけど、今は別のマシンもあることだし、bareリポジトリをはさむことにした。
bareリポジトリをはさむことで、途中までいじくった状態のを別のマシンからチェックアウトしてその続きの作業をしたりとか、そういうことができるようになった。
今までもできなかったわけではないが、bitbucketの方に変更を送信したりしないと無理そう。中途半端なのは送信したくないしなぁ。
とは言っても、別のマシンで作業する機会なんて滅多にないが。コンセントを使える喫茶店とか近くにあるなら、たまには気分転換で、そこで作業するのも悪くないとは思うのだけど。
*
今気付いたが、std::unique_ptrのtypedefを統一するのやってなかったわ。やってもやらんでもインターフェース変わらんし、まぁいっかー
次の作業を終えて、pushする時に含めようかな。
まだ本調子ではない。
*
名前の変更、大体の方針が決まった。
昨日書いたdp::Less以外にも、インスタンスの削除をdp::Freeというテンプレートファンクタでまとめることにした。
処理としてはdp::free()という関数を呼び出すだけだが、オーバーロードによりdpの構造体全てに対応できる。
あと、std::shared_ptrの生成をdp::shared()というテンプレート関数でまとめる。
std::shared_ptrのdeleter設定はコンストラクタの第2引数であり、std::unique_ptrのようにテンプレート引数で指定することができない。そこで、各構造体ごとにdp::なんちゃらShared()という関数を用意し、deleterを設定したstd::shared_ptrを生成していた。
今回、deleterをdp::Freeに統一したので、これもテンプレートで1つにまとめられる。
すっきりまとめられて気分がいい。
*
std::unique_ptrのtypedefも、deleterをdp::Freeに統一した関係でテンプレートを使ってより簡潔にできる。が、それにはC++11で追加されたエイリアステンプレートを使う必要があり、vs2012はそれに対応していない。vs2013で対応するらしいが。クラステンプレートを使えば同じことはできるけど、::typeとか書くのなんか嫌いなんだよなぁ。
色々忙しかったでした。
*
関数やらファンクタやらの名前を修正している。
今まで、キーデータの比較にdp::なんたらLessというファンクタを用意しておいて、それを使うことでstd::unique_ptrやらstd::shared_ptrやらでラップしたキーデータをstd::setにつっこんだりできるようにしてたんだけど、改めて見てみたら処理自体はどれも同じなわけで、dp::Lessという1つのファンクタにまとめることにした。
テンプレートと関数のオーバーロードとうまく利用して、コードを簡略化できたので気分がいい。
*
というのを16日辺りにやったと思うんだけど、それから今日まで色々忙しかったり疲れちゃったりしてて日記書くのおっくうになってたり、作業もできてなかったり。
今日作業するのもなかなかつらい。明日から本気出すべきか。
疲れすぎたらしく、食べ物の味もよく分からん。何食っても飲んでも味がしない。
*
別のマシンにGUI環境入れて、uimを使った日本語入力とかもできるようにした。
chromiumのビルドが失敗する。エラーメッセージを見る限り、コンパイルエラーとかではないようなのだが。ぐぬぬ
linux側のディスプレイ管理できた。
*
この次はウィンドウ生成に進む予定だけど、昨日書いた通り、関数名変更を先にやってしまおう。
*
1日に1回は日記を増やそうということで、最近は続けている。今日で途切れると3日坊主ってやつだ。
って、ああ、日付変わっちゃってる。
*
別のマシンには大体必要なものを入れた。GUI環境以外については、あとはvalgrindくらいか。今glibcをsplitdebugにして再ビルドしている。
そういえば音関係入れてないな。pulseaudioを入れておくべきか。
別のマシンにgentooをインストールしながら作業。
*
ちょっと外出する時とかでも、外で作業できたらいいなぁとか考えて小さいマシンを使えるようにしているんだけど、バッテリーどのくらいもつかなぁ。
CPUのクロック周波数を落として省電力化、とか多分できると思うんだけどどうやるんだろう。やったことがないから調べてみないと。
指紋認証機能が付いてるので、それも使えるようにしたいところではあるが、vaioだしなぁ。モジュールあるかしら。
今はgcc4.8.1をemergeしている。どのくらいかかるかなぁ。10時間はかかる気がする。
*
関数名、もっと単純にしてもいいかもしれないなぁ。
例えばゲームパッド関係だと、dp::gamePadGetButtons()という関数を使うとゲームパッドのボタン数を取得できるが、dp::getButtons()でいいかなぁとか。
別の機能で関数名が被ることがあるだろうけど、大体のものは第1引数に処理対象の構造体の参照を渡すわけで、それにより呼び出すべき関数が確定するはずだ。オーバーロードを活用する感じ。
関数と構造体から成り、例外を投げない、Cっぽいライブラリにするといっても、Cに対応する気は全くないわけだし。万が一Cに対応する場合でも、ラッピングした関数を作ればいいだけだし。
ディスプレイ関係の実装が済んだら変えてみようかな。
ディスプレイ管理を進めている。
*
前のプロジェクトから構成を少し変更している。dp::DisplayManagerに依存させなくてもいい機能を取り外して独立させたりなど。
ディスプレイの接続・切断検出機能については完了している。
現在はディスプレイの解像度や配置などの情報取得処理を進めている。
それが終わったら、ディスプレイの解像度や配置などの情報変更処理。
*
順調だと思うのだが、やはり進みが遅い気がする。前の日記から6日も経っているではないか。
モチベーションを上げるためにも、この次は音声出力ではなく、ウィンドウ生成とOpenGLを進めるべきか。
ようやくlinuxのゲームパッドのボタンやら軸やらのイベントとか対応して、linux側のゲームパッド対応は完了した。
*
windows側は後回しにして、次はディスプレイ管理か音声出力か、かなぁ。前のプロジェクトで、前者は既に作ってあるし、後者は中途半端なところまで進めている。ディスプレイ管理が先かなぁ。
ちなみにライブラリのサイズは以前より小さくなった。74KBくらいのが55KBくらいになった。例外クラスが無くなったからなのか、エクスポートするのがクラスではなく関数になったからなのかは分からんが。
今日は、地元のお祭りに行きました。最後の花火を見て、きれいだなぁと思いました。おわり
*
作り直しは着々と進行中。ペースがとろい気もするけど。
文字コード変換とか、linuxのゲームパッド接続・切断検知処理とかは完了している。
前よりいい構成になった気がする。#define DPIMPLEMENTとかいう、マクロ名をミスるだけでバグになるような記述も使わないようにしたし、ファイルの構成も、インターフェースと実装を完全に別にした。前は混ざっちゃってたからなぁ。
オブジェクト生成時に例外投げてたのもなくした。構造体を生成し、そのアドレスを返すだけの関数にしたので、失敗時にはnullを返すだけ。その代わり、対応する破棄関数を忘れずに呼び出して破棄する必要があるが、std::unique_ptr、std::shared_ptr、std::weak_ptrのエイリアスを定義しておき、それらを使えば自動的に破棄関数が呼ばれるようにしてあるので問題はない。
私としては、deleterをコンストラクタで設定するstd::shared_ptrとかあんま使う気しないんだけども。std::unique_ptrだけで十分なんじゃないかなと思っている。
でもまぁ、せっかくあるのだし、定義しておいて損はないだろうとか、そんな感じ。
今はまだbitbucketにpushしてないどころか、プロジェクトを作ってすらいないけど、まぁそのうち。
つまるところ、これはまた作り直しですねぇ。
*
C++でクラスをそのままエクスポートするとかだめだな。これはだめだ。
newを使わずに直接実体を作ると、使用するメモリ領域が固定されてしまうし、new使っても領域サイズが分かってないといかんので同じことだ。
結局、ライブラリのデータ構造は、全てポインタでやりとりすべきだった。newやdeleteをラッピングした関数を作るべきだったのだ。実装をいじくった後、ライブラリ利用プログラムを再ビルドしないと正常に動作しない場合がある、という時点でなぜ気付かなかったのか。気付くのが遅いと思った。
*
これは作り直しですねぇ。処理は大体そのまま使えると思うが、コンストラクタ失敗時に例外投げているところとかは、nullを返すようにしたい。他に方法ないしとりあえず例外投げときゃいいか、的に考えていたような気がするし。本当に必要でない限り、例外は使いたくないと思った。
核は関数と構造体から成る、ごく普通のライブラリを作ることになるわけだが、なんとか利便性を上げたいなぁ。
というのは、構造体の内部にアクセスすることが全くできなくなるわけなので、メンバ関数とか使うことが基本的にできない。同じようなことを関数の第1引数に構造体のポインタを渡すとかしてするわけだけど、それだとどうしても、関数名が構造体名を含んだなかなか長いものになってしまう。
なので、構造体のポインタをメンバとして持つ、ラッパークラスのようなものを作ろうかと思うのだが、std::unique_ptrとかと絡めて、うまいこと少ないコードで扱いやすいものにしたいもんだ。
pulseaudioいじくってた。
*
ストリームAPIの使い方がようやく分かった気がする。
pa_stream_write()で波形データを書き込むというのは分かっていたのだが、ループでそれを呼び出しまくり、WAVファイルの音楽を再生しようとしてもうまくいかなかった。
最初はうまくいっているように見えるのだが、10、20秒程度すると再生が途中で終わってしまう。pa_stream_write()で書き込んでも再生されない。
色々試した結果、一言で言うならpa_stream_drain()を呼び出す必要があるという結論に達した。
だがしかし、ただ呼び出すだけでは不十分。pa_stream_drain()の処理は非同期に実行されるため、pa_stream_drain()を呼び出し、処理が返ってきたからといって、処理が完了しているわけではないのだ。
そこで、pa_stream_drain()を呼び出してから、次にpa_stream_write()を呼び出すまでに、pa_stream_drain()の処理が完了させる必要があり、つまり待機処理が必要になる。
具体的には、pa_threaded_mainloop_lock()とpa_threaded_mainloop_unlock()をpa_stream_write()、pa_stream_drain()の前後に入れ、pa_stream_drain()の後にpa_threaded_mainloop_wait()で処理を待機させる。
で、pa_stream_drain()の処理が完了…したかは知らないが、正確にはバッファに空きができればいいわけなので、pa_stream_set_write_callback()で、ストリーム書き込み時のコールバック関数設定をあらかじめ行なっておく。そして、そのコールバック関数内でpa_threaded_mainloop_signal()を呼ぶ。
というわけで、pa_stream_write()、pa_stream_drain()、pa_threaded_mainloop_wait()と呼び出し、pa_threaded_mainloop_signal()をストリーム書き込み時に呼び出す、という流れになる。
しかしながら、これは検証のためにそれなりに適当にやったので、実際にはチェック処理とか入れるべきだと思う。
例えば、pa_threaded_mainloop_wait()とかpa_threaded_mainloop_signal()とかは、引数がpa_threaded_mainloop_mainloopのポインタしかない。つまり、ロックが1つしか存在しないため、pa_threaded_mainloop_wait()から処理が復帰した時に、チェック処理を入れ、場合によっては再度待機するとか、そういう風なことをする必要がある。
今回の場合は、バッファに空きがあるかどうかが問題なので、pa_stream_writable_size()を呼び出し、書き込めるかどうか確認する必要がある。
そもそも、pa_threaded_mainloop_wait()を使わなくても、std::mutexとstd::condition_variableでいいんじゃないか、という気がしないでもない。C++11以前は環境非依存な同期処理がなかったから、用意されたもんだと思うし。C++11の環境非依存同期処理を使える今、わざわざ使う必要もなさそう。
pa_threaded_mainloop_lockの処理、見てみたけど、結局pthread_mutex_lock()呼び出してるだけだったわ。waitとかもpthread_cond_wait()呼び出してるだけだった。じゃあいいわC++11のやつで。
*
とまぁ、これ調べるのも一苦労だ。サンプルねぇし。audaciousやらvlcやらmplayer2やらのソースを読んだが、今回の問題には大した助けにならなかったし。
疲れた。
*
androidアプリ作るの疲れた。飽きた。優先順位は低いわけだし、そろそろdpの方に戻るべきか。
dpよりもビルドに時間がかかるのと、できたものの動作確認をするのに時間がかかるのがとてもめんどくさい。すごい勢いでモチベーションが削がれていく。
しかも、XMLとかいじくると稀によく、ビルドして動作確認しようとしても不正終了する。そういう場合、1回クリーンしてビルドし直さないと直らない。ただでさえ時間がかかるのにめんどくさすぎる。
というわけでまた今度。
*
前回の日記から1週間以上経っているなぁ。さすがに時間使い過ぎた感。
antroidは機能縮小した。
*
で、androidのサービスをアクティビティからバインドしてどうのこうのというのを試してみたんだけど、今回は使えなさそう。
サービスは使うけど、通信はソケット通信かな。アクティビティからのバインドは、アクティビティからサービスの機能を使う、ということが目的なら便利に使えそうなんだけど、1対1の通信を確立するとか、そういうのには向いてなさそうな空気。
アンバインドした時に、どのバインドをアンバインドしたのか、それが判別できない。putExtra()とか使って、IDみたいのを作ればできないこともないだろうけど、絶対に被らないという保証がどこにもない。
また、既にバインド済みの時に別のバインドを確立すると、サービス側にバインドしたことが通知されないっぽい。もちろんアンバインドも通知されない。
そんな感じ。
あーでも、リモコンとの通信には使わないけど、バインド自体は使うかな。サービスと通信するためのものは別のライブラリプロジェクトにまとめるけど、通信処理の実装はライブラリプロジェクトに含めない。含めるのはインターフェースだけで、実装はサービスから取得する形にしたい。通信処理に修正が入ったから、ライブラリプロジェクトを使ったプロジェクトに再ビルドが必要、とか嫌だし。
*
なんとなくまとまってきたぞ。
antroid、機能縮小しようかなぁ。
*
ビルドとかクリーンとか、SDKに付属してるbuild.xmlに搭載されている機能を再実装するのは無駄だと思った。現時点でリリースビルドすら実装できていないし、実装してもSDKの更新で処理が変更されるかもしれないし。それにいちいち追随するのはめんどくさすぎる。
でも、プロジェクト作成ウィザードやアプリの実行、logcatの開始など、SDKに付属しているbuild.xmlに存在しないが自動化でより便利、簡単に扱える機能は使いたい。なので、そういった機能だけに絞ろうかなと。
今までビルドやクリーンもサポートしていたのはなんでだったかなと思ったら、デバッグビルドとリリースビルドの出力先を分けることが目的なんだった。しかし、最近依存するライブラリプロジェクトとリンクできるようにしたところ、クソみたいな原因で正常に動作しなくなったため、出力先を分けられなくなったんだった。
思い出したらまた嫌な気分になった。なんだよあのクソ実装は。antの独自タスクに、出力先がbinとハードコーディングされてやがる。定数が使われてるからハードコーディングとはちょっと違うが、実質的に同じようなもんだ。build/debugとbuild/releaseに分けたかったのだが、bin以下にライブラリのjarがねぇよとか言い出してこける。忌々しい。
独自タスク内はさすがにどうしようもない気がする。なんとかして定数を、リフレクションとか使って書き換えられないかなぁ。
*
androidからいじくるために、wiiのクラシックコントローラを買ってきた。中古。
なぜかLとRがアナログらしい。あとZLとZRの位置がすごい。人差し指が疲れそうだ。PROも入手したいところだが、PROじゃない方でも大体同じように使えるだろうし、まぁいいかといった感じ。欲しくないわけではないが。
*
androidのアプリ間通信、アクティビティとサービスは接続できるらしいなぁ。その辺知らんかった、というかサービスについて詳しく知らない。なにやら、接続してデータをやりとりできるようだ。アクティビティからサービスのメソッドを呼んだりできるらしい?
ソケット通信にしようかと思っていたが、アクティビティとサービスの連携がうまいこと使えそうなら採用しようかな。ポート番号とかのめんどうなことを決めなくてもよくなるだろうし。
その辺、検証してみよう。
1ヶ月毎に別ファイルにすることにした。
*
昨日あんなことを書いておきながら今日もandroidからwiiリモコンをいじくろうとしている。
BluezIMEのソースを読んで接続方法は把握した。ペアリング?そんなものはなかった。
想像はできていたが、なかなか無理矢理な方法だった。android.bluetooth.BluetoothSocketの非公開なコンストラクタを、リフレクションで呼び出す。SDKのリファレンスにも当然コンストラクタの仕様なんぞ載ってないし、その辺の実装が変わったら簡単に動かなくなりそうだ。
私の使っている環境はandroid4.1.2だけど、4.2からは内部で使っているBluetoothのライブラリが別物になるらしい。その辺の変更でインターフェースが変わってたら動かなくなるなぁ。
で、接続して、LEDの制御をするところまでできた。あとは入力データの解析とかすればいいわけだが、かなりいい加減に作っていたので、この先を進めるにはちょっと整えないとめんどうなことになりそうだ。
入力データの処理は別スレッドでやるべきだと思うが、スレッド分割することを全く考慮していないので、ひどいことになりそう。何をどうすればいいのかも分かってきたし、本格的に作り始めるべきか。
そういえば、BluezIMEのソース読んでたら、LEDの点灯は右端の1つを点灯させるの固定になってて残念な気分になった。せっかく4つあるのに。まぁ、BluezIMEはwiiリモコンだけを制御するわけじゃないし、LEDの点灯が全部同じだったからといってソフト側にはなんの不自由もないわけだし、いいんだけど。
oh、androidでwiiリモコン動かすの難しいネ。
*
wiiリモコンと端末をペアリングするところからしてうまくいかない。
androidはPINの入力を要求してくるんだけど、PINは不要らしい?でも空入力とかできないし。
既存のアプリのソースを見てみたかったが、なかなか見つからなかった。
幸いにも、BluezIMEというアプリはプロジェクトのページでソースが公開されていた。
ただアプリをインストールするだけでは、wiiリモコンは使えない。何やら、l2capなるものが使える端末でないと動かせないらしく、上のプロジェクトのページで配布されているHIDEnablerをインストールすることで使用可能になる。
ソースを読んでみようと思ったのだが、なんかめんどくなってきた。そんなに量が多いわけではないのだが。
端末に搭載されているペアリング機能を使わずにペアリングを行なうっぽいところからしてめんどくさい。どこに書いてあるんだろう。
*
とまぁソースを読むのがめんどうで一気にモチベーションが下がったので、早々にdpの方に戻ろうかなぁ。昨日色々書いたのを参考に進められるかもしれん。
でもまぁ謎のままにしときたくないというか作りたいので、暇を見つけては解読することにしよう。
antroid直した。
*
sdkに含まれているbuild.xmlを見ていたら、なんか、lintなるツールが追加されたらしい。よく分からんが、このページによればなんか知らんがすごそう。
>これは、Androidプロジェクトのソースコードの潜在的な不具合を発見するためのものです。
なんか知らんがすごそう。上のページに色々書いてあるようだが、そこまでしか読んでないから全然分からんけど。
*
というわけでビルドとかできるようになったので、少しandroidアプリを作ってみようかしら。
dpの方は、pulseaudioを使った音声出力デバイスの列挙とか、接続・切断の検知をできるようにしたところで、一応一段落している。
次は音声を出力したいが、クラス名とかどうしたもんかなーとか。AudioPlayerか?とも思ったが、あまり色々な機能を提供する気がない。というか、コンストラクタでopen()して、write()で音声を出力して、デストラクタでclose()する、というのを想定している。これを指して、AudioPlayerというのは適当でない気がする。
となるとAudioWriterかなぁとも思ったが、しっくりこない不思議。ううむ。
dp::AudioWriter(仮)を作るにしても、どうしても出力デバイス管理クラス、dp::SpeakerManagerに依存しなければならない、多分。
dp::SpeakerManagerは、音声出力デバイスが接続されると一意に対応するdp::SpeakerKeyを作ってイベントハンドラを呼び出すとかするんだけど、音声を出力する場合、dp::SpeakerManagerのメンバの、pa_contextが必要になる。
できることなら、
dp::AudioWriter writer( speakerKey );
writer.write( ... );
みたいな感じでやりたいんだけど、pa_contextが必要だからdp::SpeakerManagerと関連付けないといかん気がする。
しかし私としては、デバイスの検出が済んだらdp::SpeakerManagerを破棄して、dp::AudioWriterを作る、とかいう風にもできるようにしたい。依存関係を持たせるとそれができなくなる。
pulseaudioのデータは、基本的に参照カウントで管理しているっぽいので、pa_contextのポインタだけをdp::SpeakerKeyに持たせる、とかいう方法もいけるのかなぁ。いけるなら、dp::SpeakerKey生成時に参照カウントを増やし、破棄時にカウントを減らし、dp::AudioWriter(仮)の時も同じようにすればいけるのかなぁ。
などなど。あ、そういえば非同期な音声出力をまだ試してないなぁ。pa_streamとかっぽいんだが。
*
などと長々書いたが、色々と考えはあるものの、あまりモチベーションが上がらない。のでandroidアプリの方を進めてみようかな。
androidどころか、javaからして久しぶりだなぁ。jdk入れてなかったし。jreも入れてなかった。
とりあえずbluetoothデバイスをいじくる系のアプリを作ろう。まずはペアリングから。前に、android端末同士をペアリングして、テキストを送受信する簡単なアプリを作ったことがあったが、ソースは無くしてしまったようだ。残念。
久々にandroidアプリをいじりたくなった。
*
今の環境にsdkやらndkやらを入れていなかったので、まずは環境構築から。sdkを入れた後に、sdkに含まれているツールを使って色々落とさないといけないのがめんどい。
前に作ったantroidが使えるかどうか試したが、プロジェクト作成は使えるもののビルドとかがうまくいかなかった。過去の環境にあるsdkと、新たに配置したsdkに用意されているbuild.xmlに差異があったので、ant周りにも変更が入っているようだ。修正が必要だ。
*
目的は、wiiリモコンを使えるようにする。で、wiiリモコンで遊べるゲームを作りたい。
wiiリモコンの制御自体は、すでに他の人がアプリを公開しているようだが、見た限りではどれもこれも、wiiリモコンをbluetoothキーボードとして使えるようにするとか、そんな感じ。そんなのは邪道だ。
考えているのは、アプリとwiiリモコンが直接通信し、制御する。そのアプリに対し、wiiリモコンを使いたいアプリが接続し、制御アプリを介してwiiリモコンを制御する、みたいな?wiiリモコンの鯖を作るような感じか。
鯖との通信はソケット通信かな。androidに搭載されているプロセス間通信で、他にいいのがあればそっちを使うんだけど。
まぁ、優先順位は高くない。ビルドが通らないんでantroidを修正しないといかんし、現時点でもsdkのツールを使ったダウンロードが遅すぎて終わる気配がしないし。これまた気分転換する時に進めようかと。
*
ぷよぷよ楽しい。
無駄に疲れた。
*
なんとか、デバイスを列挙する方法とか、接続・切断を検知する方法は把握した。
しかし、ドキュメントが分かりにくい。これなんだけど、英語なのはまぁしゃーない。そこまで期待してない。
まずファイル名が分かりにくい。これ。あれ?それほどでもないか?ファイル名というより、どの機能がどのヘッダファイルにあるのかが分かりにくい。デバイスの列挙や接続・切断の検知関係はintrospect.hとかいうヘッダファイルに書かれてるし。なんやねんintrospectて。日本語でおk。
pavucontrolのソースを読んでpa_sink_infoとかpa_source_infoとかいう、デバイスの情報が入っている構造体があるのは分かったが、どのヘッダファイルに定義されているか分からなくていちいちgrepかけるはめになった。
で、あとはドキュメントの内容が簡潔すぎる。関数1つにつき説明は大体が1行、というか一言、引数や戻り値については説明がない。それで使えるのならいいのだが説明ないと分からんこと多いし。情報が入ってそうな構造体のポインタがnullだった時とかどうしようと思ったわ。
ドキュメントを自動生成とかで作るのは構わないが、役に立たんものを見せられても困るわ。関数の名前とかもいまいちピンと来ないし。その辺気を付けて、ドキュメントとか作らなくていいんで作り直して欲しい気さえする。
結局、役に立ったのはpavucontrolのソースだった。
*
あーあ、なんか無駄に疲れた。疲れたというかやる気を吸い取られた感。
pulseaudioをいじっているが、うーん。
*
ドキュメントを読んでも、シンプルなAPIと非同期なAPIの2種類がある、ということ以外はいまいち要領を得ない。
で、シンプルなAPIを使ってWAV形式のファイルを鳴らす、というのはできた。
しかし、デバイス名を指定する箇所はあるのだけれど、何を入れたらいいのか分からん。文字列なんだけど、pavucontrolで表示されるデバイス名入れてもだめっぽいし。
というかデバイスの列挙とか検知とかどうやるんだ。pavucontrolではできてるんだから、何かはあるんだろうけど、最悪udevでやることになるのかなぁ。
pavucontrolのソースを読むのが一番手っ取り早いだろうか。
*
と、ここまでが昨日書いた内容。書いている途中でめんどくなってしまった。
今pavucontrolのソースを読んでいるが、pulseaudio自体にデバイス管理の機能が存在しているようでよかった。
非同期なAPIとデバイス管理、どっちを先にやるかなぁ。なんとなくシンプルなAPIでも問題なくできる気がしないこともないのだが、シンプルなAPIだと複数の音声を出力するためには同時に出力する数だけ接続を確立する必要がある。
これがalsaならなんだそんなこと、とあまり気にしないのだが、pulseaudioの場合、接続1つ1つについて、音量を調節できたりする。自前で音量調節のコードを書かずとも、pavucontrolを使えば調節できる。で、同時に鳴らす数だけ接続が存在するので、例えばBGMとSEの音量を個別に調節できる、というのは便利だろうけど、実際にはSE1つ1つ、異なる音量調節をするような感じになってしまう。多分。さすがに分けすぎである。
なので、利便性とかを考えた場合、やはり非同期なAPIは使う必要があるだろうなぁ、とか。
でもまぁ、とりあえずはデバイス管理が先かな。シンプルなAPIや非同期なAPIとは独立した機能っぽいし。
昨日からまたRaspberryPiをいじくり始めている。
*
前に作った環境は、glibcのバージョンを上げたら戻せなくなって、それが原因で色々なところで詰んでしまって、にっちもさっちもいかなくなってしまった。
なので、環境から作り直している。無論gentooである。現在gcc4.8.1をemerge中。昨日の日付が変わった頃から始めたと思うので、多分日付が変わる頃には終わるだろう。
終わっても、更に長いemerge -e systemとかemerge -e worldが待っているわけだが。
前に作った環境はXをインストールしてデスクトップを使えるようにする、というのはやったけど、今回はとりあえずOpenGLを試したい。ModMyPiにある説明によれば、GPUの性能は×箱と同等らしいし。
*
今までは上のリンクにあるモデルBしかなかったんだけど、今年の4月からモデルAも発売されていて、気になるところだ。
モデルBと比較すると、LANポート削除、USBポートが2つから1つに、搭載メモリも512MBから256MBになっているなど、性能は落とされているが、その分価格も29.99ユーロから21.99ユーロになっている。
更に、必要な電力も700mAから300mAになっていて、これはUSB2.0の供給電力である500mAより少ないので、パソコンのUSB2.0ポートに接続しても安定して動作するんじゃないかな、と思っている。モデルBはパソコンのUSB2.0ポートだと安定しないからなぁ。これを使っている。
ネットワークに繋がなくてもいい時はモデルAを使って、ネットワークに繋ぐ必要がある、パッケージのアップデートとかする時はモデルBを使う、とかそういうのを思い付いた。
*
そういえばこれもまだ試してない。せっかく買ってあるのにもったいない。ここに導入方法が書いてあるが、ドライバモジュールはカーネルのメインラインには取り込まれていないようだ。めんどいなぁ。
*
dpは、とりあえずウィンドウ周りは後回し。ディスプレイの解像度変更に関連して、フルスクリーン表示にするためにタイトルバーなどの装飾なし、リサイズ不可能なウィンドウもサポートしたいけど、フルスクリーンではない、装飾ありのウィンドウも絡めた構成がまだはっきりしない。
前のプロジェクトだとウィンドウのサイズは変更できなかったけど、フルスクリーン時に解像度を変更すればそれに合わせてウィンドウサイズも変わるようにしたいし、それなら装飾ありの場合はウィンドウサイズを変更可能・不可能を選べてもいいんじゃないかなぁとか。変更可能なウィンドウを途中から不可能に変えたり、その逆とかもありかなぁとか。
それらが実際にうまく機能するかとか、そもそも実現可能かとか、色々試してから作りを決定したい。
*
で、音声出力周りを先にやっつける。前のプロジェクトに用意したAPIは、少し大げさと言うか、ライブラリレベルなのに余計な仕組みを作ってしまった気がするので、今回作るのはもっと単純にする。
前回作ったのは、
dp::AudioPlayer player( device, []( std::vector< char > & _buffer ){ /*バッファに出力する音声データをつっこむ処理*/ } );
いい加減だけど、こんな感じ?dp::AudioPlayerのコンストラクタでスレッドが作られ、インスタンスが存在している間音声を出力する、というもの。
スレッドの中で第2引数のファンクタがバッファに出力データをセットし、第1引数の音声出力デバイスに出力する、ファンクタがデータを返さなくなったらdp::AudioPlayerを消滅できる、という感じ。ファンクタがデータを返し続けている間は、消滅させようとしてもデストラクタで処理待ちになる。
便利と言えば便利なんだけど、環境毎の差異を吸収するライブラリでやることではなかったなぁ、と。なので、今回は単純に、音声出力デバイスをopenして、出力データをまとめたバッファをwriteして音を鳴らし、終わったらcloseする、とか、そういうのでいいんじゃないかなとか。より便利なのは、上位のライブラリに作ればいい。
そうなると、前のプロジェクトじゃ使えなかったXAudio2とか使えるんかな?どういうAPIだったか忘れてしまったので調べ直す必要はあるが、WASAPIはサンプリングレートの変換処理とか必要だったからなぁ。そういうめんどい処理が省けて、問題なく利用できるなら使いたいところだ。
疲れた。
*
なんとか、本登録画面のURL(仮)を書いたメールを送出するところまで出来たっぽいんだけど、疲れた。
ソースはpushしといたけど、続きはまたの機会にしようと思った。起きたらdpの方進める。
何を作ればいいのかも分かっているし進められないことはないのだが、作業量が膨大になりそうだけどその割にできるもののスケールがそれに見合ってない気もするし、なによりなんかつまらん。やはりwebアプリは性に合わんらしい。
またやりたくなったら進めることにする。
*
Goはおもしろかったんで、たまにちょこちょこいじろう。
データストア苦戦中。
*
大体分かってきた。もうちょいでなんとかなりそうな気がする。
初期の認識では、リレーショナルデータベースに例えると、テーブル1つに1つしか主キーを持てず、トランザクション内では同時に1テーブルの1レコードにしかアクセスできない、と思っていたので、こんなんどうやりゃいいんだと思っていたが。
リレーショナルデータベースとはまた別物なので、リレーショナルデータベースには存在しない部分を知っていったら、なんとかなりそうな気がしてきている。
確かに、リレーショナルデータベースで言うところの主キー、重複不可なキーデータは1つのデータ集合につき1項目しか持てないんだけど、というより強制的に持たされるが、キーデータ自体の参照を項目に加えられるので、専用のデータ集合を別に定義すれば解決できる。
トランザクション内では同時に1データしかアクセスできないが、データ1つにつき1つだけ、親キーを指定することができ、親子関係にあるデータならアクセスできる。
とか書いても、実際にやってみないと大体理解できないのだけど。
*
リレーショナルデータベースは扱ったことあるけど、その経験が逆に引っかかってしまって、うまく理解できていなかった気がする。
あと、GAE/Goのドキュメントが英語しかないのも理解が進みにくい1つなんじゃないかなと思っている。機能を把握しきれていない。
トランザクション内じゃデータ検索クエリを実行できないんじゃないかなとか思ってたし。親キーを指定してフィルタリングかける関数とかあるんじゃないの。まだ使ってないが、クエリ投げた時に出たエラーメッセージからしてうまくいきそうな香りがぷんぷんしてるわ。
*
明日中には、まとまった形になればいいのだけれど。把握できていなかったことを知れば知るほど、よりよいアイデアが湧いてくるからまとまらなくて困る。
データストアで詰まっている。
*
これからまた色々試すけど、データストアへの理解がいまひとつ。
GoのデータストアにアクセスするAPIは、GQLとかないっぽい?なんか、全部関数で提供されているような。
それはともかくとして、データストアを扱えないことにはGAEにデータを保存できないわけなので、がんばらんと。できるだけ早く。
画面が1つできた。
*
なんとなくローペースな気がするが、webアプリは経験が浅いので仕方がない面もあるのだろう。焦らずやろう。
画面ができただけで、鯖側は完全に未実装。起きたら作る。
作ったのは、サイトに記事を追加したりするアカウントを追加するための、招待メール送信画面。招待メールに一定時間有効なURLが記載されていて、そこにアクセスすると対応するアカウントが作られるとか、そこらによくあるあれだ。といっても記事を追加するのは私だけなんで、この画面を使うのも私だけ。
なので、シンプル&手抜きな画面になった。こんな感じ。この画面にアクセスするためには、サイトの管理者としてログインしなければならない。その仕組みはGoogleが用意してくれているのでらくちん。その上、この画面は私がアカウントを追加する時に有効にして、以降は無効化、アプリに処理を含めすらしないので、セキュリティ的にも問題はないと思う。
サイトの管理者を乗っ取った上、私がアカウントを追加するために、この画面を有効にした時を狙えば、私以外のアカウントが作られたり、ということは可能だろうけど、まぁ不可能だろう。
私しか使わないので、入力したメールアドレスが正しいかどうかなんてチェックしていない。めんどいし。一応、招待メール無効機能とか付けてるけど、多分完全に死に機能。
あと、この画面はJavaScriptが有効じゃないと機能しない。JavaScriptを使わない版も、作れなくはないだろうけどめんどいしなぁ。私が使わんだろうし、多分作らない。
*
とまぁ、私しか使わないのをいいことに手抜きしまくりだけど、他の人も見る画面についてはそんなことする予定はないので安心するといい。シンプルなのは多分直らんだろうけど。めんどいし。
ちなみに、今日作った画面はtekuto.netのものではない。記事とかなんやらのデータはRESTfulなAPIを提供する他の鯖アプリで管理し、tekuto.netはそのAPIを利用するだけのクライアントアプリにする、とかそういう想定。妄想。
やろうとすれば、例えばAndroidアプリとしてクライアントアプリを作って、そこから鯖アプリにアクセスしてどうのこうの、とかもできるようにする感じ。やらんだろうけども。やったとしても私専用。他の人がわざわざ専用アプリで閲覧するメリットが感じられん。
RSSリーダ使えよ。RSSの存在自体知らない人向けなの?
私もRSSくらい配信すると思うんで、ヲチしたい奇特な人はそれ利用するといいと思う。
*
ちなみにRESTfulなAPIを提供する鯖アプリのリポジトリはここなんだけど、内部の処理もできてからpushする予定なのでまだ空っぽ。予定通りにいけば明日中にはpushできるんじゃないかな。
Goをやっている。
*
日本語情報はこっちで、wikipediaはこっち。以前から興味があったし、GAEで使えるんだし、ということで使うことにした。Pythonでもよかったんだけど、せっかくなので新しい物にチャレンジ。
文の末尾にセミコロンがいらない、というところで若干つまずいた。厳密にはいらないのではなく、コンパイラが勝手に付けて解釈しているらしく、まずいところで改行すると文の途中なのにセミコロンが入ってしまうような感じになって、コンパイルエラーになっちゃったりしてた。
でもまぁ、その辺理解したら、いつも書いているような、関数の宣言や呼び出しで、引数1つに1行使う形式で書くことができた。推奨はされなさそうだけど、diff取った時に変更点が一目で分かって便利なので仕方がない。
*
変数への値の代入演算子(=)とは別に、初期化演算子(:=)が用意されていて、初期化演算子を使えば
var 変数名 型 = 値
とか
var 変数名 = 値
とか書かなければならないところを
変数名 := 値
にできるところとか、地味にうれしい。:=で既に存在している変数を初期化しようとしたり、=で存在しない変数に代入しようとしたりするとコンパイルエラーになる。
なので、PHPやPerlなどのような明示的な変数の宣言が不要な関数でありがちな、変数に値をセットしようとして、変数名間違えちゃって新しい変数が作られちゃった、値をセットしようとした変数の値は変わってないので動くんだけど動作がおかしい、とかいうびみょうに問題箇所を見つけにくいバグが起こらず、それでいてそれらと同等の簡素な記述ができる。いいことだ。
*
例外処理は存在しないんだけど、関数を抜けた時に処理を行うようにするdeferというものがあり、これでRAIIと同じようなことができるので大して困らなそう。
スレッドは存在しないみたいだけど、ゴルーチンというものがあって、それで処理の並列化ができる。チャネルというものを使えば、同期を取ることもできる。らしい。どっちもまだ使ってない。deferもだけど。
パッケージ外へ提供するもの、しないものの設定方法がちょっとユニーク。先頭文字が大文字ならパッケージ外からアクセスできる、小文字ならできない。らしいんだけど、小文字のものでもアクセスできる場合があるんだけどいいのかなぁ。
名前によって外部からアクセスできるかどうか決める、というのはちょっとPythonぽいな、とか思った。あっちはほとんどただのコーディング規約だけど。
構造体とかインターフェース周りもまだ触っていないんだけど、JavaやC++のクラスとかとはまた違った空気がしていて興味深い。
*
dpをGoで書くのもありかもしんないなぁとか思った。
おはようございます。
*
諸事情によりサーバを移転しました。現在はサーバというか、Google App Engineの1アプリとしてサイトを稼働させています。
*
ある日、レンタルスペースに配置したMovable Typeにログインしても、ページ遷移したらすぐログアウトしちゃうようになってて、困っていた。
原因はDBの容量がいっぱいになっちゃてて、セッション情報すら持てないような状況だった、ってことなんだけど。
サイトのコントロールパネルから、DBの全テーブルに最適化をかければいくらか容量空くだろう、と思って最適化しようとするも、insertできません><権限ないんで><とかわけのわからんこと言われるし。
deleteは通るし、とりあえずスパムコメントのレコードだけでも消しておくか、なんか変わるかもしれんし、とかやって放置していた。
*
で、今日になって、dpにまた1つ機能ができたので、それについて記事書くかーとMovable Typeにログインしようとしたら404。サイトのコントロールパネルにログインしたら、アカウントはブロックされていますだって。
私だってなんとかしたかったがどうしようもなかったからしょうがないじゃないの。どうすりゃよかったと言うのだ。問い合わせ?英語むり。
そんなわけで、以前から考えていたGoogle App Engineへの移転に踏み切ったわけです。DBの容量10MBとか最初から無茶だったんだ。アカウントはブロックされていますのページに可能な対応と思われることが色々書かれていたが英語とか読めねぇしもうたくさんだ。海外の鯖なせいかレスポンス性能もよろしくないというか稀によく落ちてるし。
Google App EngineはGoogle先生が運営してるんで、レスポンス性能は前よりずっといいと思う、多分。稀によく落ちることもないんじゃないかな、多分。
*
Google App Engineに移転したといっても、現時点では見ての通りタグ打ちの静的なページだけ。staticの中ね。
そのうち、タグ打ちしなくてもブラウザ上から投稿だとか、投稿に対してコメントだとか、そういうのできるようにする予定は一応あるんで。
でもめんどいんでしばらくお待ち下さいと思ったが、dpがちょうどいいところだし、進めてみようかなぁ。
飽きたらすぐdpの開発に戻るが。
*
とまぁ、しばらくはこんな感じで、昔みたいな1ページ運用になるのでよろしくお願いします。