本稿は自作エディタをつくる Advent Calendar 2016の17日目です、レポジトリはこちら
cursesってなんだろう
しばしばこの連載のコメント欄で話題にあがるcursesとはいったいなんだ・・・?ということで調べてみます。
もうすぐ動きそう!って気持ちがあって後回しにしてたんですよね。
とりあえず、cursesの概要もわからないのでwikipediaから。
wikipediaから引用すると
ほとんどの実装ではterminfoを使っており、一部はtermcapを使っている。
お!3日目にでてきた、rawモードとcookedモードを切り替えるやつがterminfoですね。
3日目は自分としては結構難しかったから(検索ワードが全然わからなかった)、抽象化してくれると嬉しいです。
ただ、該当部分はマルチバイト文字の扱いも難しいので(7日目・8日目)、古いライブラリ?だとちょっと不安です。
あっ、下に多バイト文字対応してるって書いてますね。大丈夫かも。
プログラマは特定の端末装置を考慮せずに文字ベースのアプリケーションを書くことができる。
cursesライブラリは、実行時に使用している端末装置を判別して適切に制御コードを送ることができる。
いま制御文字(たぶんCtrl+何かのキー)は、ほぼ無視しているので代わりにやってくれると嬉しいですね。
一応その辺りは関数として切り出されているので、肩代わりしてくれるかもしれません。
curses では、実画面を1つ以上のウィンドウをマップしたものとしてモデル化する。
すごい。左右ペインみたいなのを作ろうとしたら、今の作りだと絶対実装できなさそうです。
(上下に分割するのは、今の作りでも出来そう)
curses に対して画面の更新を指示する。curses は内容の更新状況を調べ、実際に画面上で書き換える必要があるところだけを書き換えるような制御文字列を生成する。
僕の雑な理解だと、VirtualDOMみたいなやつですかね。画面のチラつきを抑えれそうな感じがします。
毎回全部printfすると、思ってたよりチラつきました。実装が下手だからかもしれないですが。
つまり、プログラマは画面にどう表示したいのかを文字行列で示し、curses がそれを実際に表示する作業を受け持つ。
C言語は可変長文字列が(自分にとって)難しいので、文字行列の実装イメージが思い浮かばないです。
コンソールは途中でリサイズできるし、文字は何バイトかわからないし・・・。
そこを、リスト構造+その場でprintfの方向でごまかしてやってますが、果たしてcursesだとどうなんでしょう。
うまいことコンソール表示高さ・幅全体が格納できる固定長2次元文字列をつくるのかな?
cursesのインターフェイス仕様を調べる
とりあえずwikipediaで概要を予習したので、頑張って実際の使い方を調べます。
・・・他の言語にバインドされてるんですね。可変長文字列型があるプログラミング言語使った方が楽だったのでは。
これからエディタを作り始める人がもしもいたら、可変長文字列型があって、
さらにunicodeのハンドリングをある程度肩代わりしてくれるプログラミング言語がオススメです。たいていの言語は当てはまるような気が・・・。
まぁ、今更なので自分はC言語で進めます。
調べるうちに、cursesのインターフェイス仕様はわりと単純なものが多くて、Wikipediaを読んだときの印象よりは使いやすそうと思いました。
自分が理解した範囲では、作っているエディタとして抑えるポイントは4点です。
- 1日目のコンソールクリア・文字に色をつける等は移植性を考慮して簡単な関数を呼ぶだけでできる。
- たまたま自作関数と置き換えるのが容易(レポジトリのconsoleフォルダ部分が該当し、インターフェイスが似てる)
- 表示は、仮想スクリーン(仮想的なコンソールみたいなもの)に書いて、その後
doupdate関数
を呼ぶといい感じに反映される。- そのためcursesを使ってなくても、この連載のように
毎回clear
+1行ずつprintf
であればcursesに移植しやすい。 - printfをinsstrに置き換えて、行数を指定すればよい。自作エディタは余白調整用に表示した行数を数えているので、やればできそう。
- そのためcursesを使ってなくても、この連載のように
- 3日目に意味がわかってなかったgetchは、cursesで提供されている。
- もしかしたら、標準ヘッダにgetchがある環境もあるかもしれないが、macだとcursesが必要そう。
- キー入力をgetcharからgetchに置き換えると、おそらく制御文字の文字コードが環境によらず一定となる。
- ただし、C言語+utf8であれば8日目のような後続文字判定は必要で、cursesは該当部分を肩代わりしてくれない(自信なし)
- 自作エディタでいうと、getcharをgetchに置き換えた上でcommand_parse.cにベタベタ書いているif文の文字の定数を置き換える
- rawモードとcookedモードはcursesにやってもらうとよい。終了とかは邪魔しないrawモードとして、rareモードっていうのがあるらしい。
という感じです。
cursesを使うと楽になる気がしますが、それは主に移植性の部分が多いはずなので
- まずはmac + macの端末エミュレータ + bashで安定動作
- その後、移植性確保のためにifdefでcursesも選べるようにする。
そんな感じでやっていこうと思います。
16日目にズルズルでてきたバグはmallocとかが下手なのが原因なので、cursesは関係ないですね。先にそこを直さないと。
これからエディタを作る人向けのアドバイスとしては
- まずcurses及びバインディングの動作確認をする。
- 50文字の幅+30行の高さとか、決め打ちのつくりにする。
- コンソール制御(色・コンソールクリア・入力モード)はcursesの関数をそのまま呼んで作る。
- cursesは(特にC言語以外の言語では)抽象度が低くて汎用度が高いと思うので、ラップして自分の使いやすい関数にしていく
- printf部分をcursesをラップした関数に載せ替えていく
- 決め打ちを外していく
のがいいかなぁと思いました。
いきなり仮想コンソールとか使うと問題の切り分けが大変かもしれないです、たぶん。
今日のまとめ
- cursesを調べました
curses、もし初めから使ってれば4日分くらい作業量が減った気がしますね。
もしくはC言語を選んでなかったかもしれません。
これを読んでる人がエディタを自分の好きな言語で作りたいときは、cursesはオススメです。
1日目に書いたなんとなくコンソールと相性が良さそうなイメージがあるC言語
は、cursesがあればあまり関係がないです。
C言語は自分にはとっつきにくいイメージなので、他の言語でできるならエディタを作る人がもっと増えるかもしれない!?(作ったらぜひ教えてください!)
明日は16日目に見つかったバグを直すぞ!(更新遅れてるので、いまから直します)