初心者ライダーが初心者ライダーに送るクソ雑魚初心者ライテク

本投稿は静大情報LT大会(メディア・文化) Advent Calendar 2018 11日目の記事です。

コンニチハ、悟空です。
今年2018年3月に普通二輪免許を取得し、VTR250に乗っている初心者ライダーです。
乗るのもいじるのも、バイクの全てにドハマリしてしまい、他が手に付かなくなってしまいました。こんなにハマった趣味はプログラミングとバイクだけですね。
マルチのSSに乗り換えたいという思いで死にそうな今日この頃。

さて本投稿では、そんな初心者ライダーである私が畏くも恥を忍んで同じ初心者ライダーに、様々なメディアで学んだ山道で役立つかもしれないライディングテクニック、略してライテクについて書くと予告したな、あれは嘘だ! 書いてるうちにライテク基本理論みたいになってしまいました。以下あくまで初心者の戯言なので、そんなこともあるのかぐらいで右から左でお願いします。間違っているところもあるでしょう。(ベテラン)ライダーの先輩諸氏におかれましてはよろしくご指摘、ご指導のほどおねがいします。
出てくる用語には特に断りなく使用しているものがありますが、ググれば一瞬でわかるレベルのものです。

ちなみにこのブログは最終記事が2014年のいにしへのブログですのでお気になさらず…

※注意
以下の内容を勘違いして公道で暴走行為を行うことは言うに及ばず違法です。
Ride 3で我慢しましょう。

  • ライテクなるものがあるといいかもしれない理由

山道、それは信号機、歩行者、通行量、対向車の頻繁な右左折が少なく快走できることが多くのライダーにとって魅力です。実家が田舎ゆえ、又、父につれられた初公道で向かったのが山道だったこともあってか、なにかと山道をはしることが多いのですが、私にとって初心者として山道で楽しくも怖いのはなんといっても迫りくるコーナーでした。山道のワインディングやヘアピンを安全に走行するべく、ライティングを向上することは自分の身を守ることに繋がるとも言えます。公道を走るのにプロライダーのようなライテクは全く必要ありません。ですが、それを知っていること、実践できることは安全なライディングを支えることはあっても、邪魔することはありません。

  • バイクはなぜ曲がる?

あなたはバイクでコーナーを曲がるときどうするでしょうか。ハンドルを切る? バンク(車体を傾ける)させる? 目線をコーナーの先に向けるとも教習所で習いましたね。はたしてバイクはなぜ曲がるのでしょうか?

下記のブリジストンによるわかりやすい解説: https://www.bridgestone.co.jp/products/tire/mc/howto/role.html
バイクが曲がる正体、それはキャンバースラスト力です。もちろん、車と同じようにタイヤの向いた方向、角度に曲がろうと働くコーナリングフォースもありますが、それにもましてバイクにおいてはキャンバースラストがコーナリングの主体となります。
簡単にいってしまえば、キャンバースラストとはタイヤの傾きにより傾けた側に生じる力のことです。ではなぜバイクにあって車にないのかというと、それはタイヤの形が異なるからです。車のタイヤは四角いのに対し、バイクのタイヤは角がなく丸みを帯びたドーナツのような形をしています。車のタイヤは自立しますが、バイクのタイヤはドーナツ型なので自立しにくい、その差が傾くか否かを決めています。つまり、どれだけバイクのハンドルを切ってもタイヤが、車体が、傾いていないと曲がりません。近年のGPライダーは車体を傾けすぎて膝はもちろん肘までもが地面に付き、まるで寝転がりながら走っているかの如くコーナリングしていますが、そこまでバンクさせるのはまさにサーキットの速度域でバイクを曲げるためなのです。
タイヤの空気が抜けたり、古かったりすり減ったタイヤは走行中ドーナツ形というよりも四角くなってしまいます。しかるにキャンバースラストが働かないので当然まがりません。タイヤを気にしなければならないのは、なにもプロライダーだけではないのです。

  • キャスター角とトレール量

キャスター角やトレール量というのは必ずバイクの諸元に書いてある重要なスペックの一つです。キャスター角はフロントフォークがどれだけ傾いているか、トレール量は、フロントフォークの延長線と路面との交点とタイヤの接地点との距離です。一般にキャスター角が大きくなるほどトレール量も大きくなります。このキャスター角とトレール量はそのバイクのコーナリングと直進安定性を決める大きな要因です。


キャスター角とトレイル、その関係について – ZRX1200R「もの」語り2ndより引用

キャスター角、トレール量が大きいバイクは直進安定性が高く、ハンドルの戻ろうとする力が強くなるらしいです。一方で車体を倒し込みにくくなります。 このようなバイクとして俗にアメリカンといわれるクルーザーがあげられます。
キャスター角、トレール量が小さいバイクは、逆に車体を倒しやすくなるそうです。このようなバイクとしてスポーツバイク、レーサー、レーサーレプリカスーパースポーツバイクなどがあげられます。
では市販のスーパースポーツバイクの直進安定性が低いのかといえば、300km/h超でサーキットを駆け抜けるためにすることはスロットルをひねることだけなぐらいなので、上の説明には自分自身納得のいかないところがあります。直進安定性があると言われるクルーザーは一般にその直進安定性を心配するほど速度も出ない/出さないのでこれもまた疑問ではあります。

  • ブレーキング

ブレーキングとコーナリングとには密接な関係があります。ブレーキングの役目は減速だけではありません。それはコーナリングへの連携です。ここで先程のキャスター角とトレール量が登場します。サーキットではコーナー進入のためのブレーキングはハードブレーキングともいわれるフロントブレーキを全力でかけるブレーキングです。そしてそのブレーキをかけたままコーナーに進入します。なぜコーナー進入手前でブレーキをリリースしないのでしょうか。それはキャスター角を小さくしてより回頭性を高めるためなのです。
ブレーキングするとフロントフォークは段々沈んでいきます。サスペンションなので当然です。そして慣性の法則が働き、フロントが沈み、リアが浮く、極端に言ってしまえばジャックナイフのようになります。ここでフロントフォークの状態を考えてみると、深く沈みこんだ状態はフロントフォークがより直立に近い状態になっている状態、つまりキャスター角がより小さくなっている状態なのです。もちろんブレーキをリリースすればフロントフォークはもとの状態にもどっていきキャスター角度はもとの角度に戻ります。またフロントブレーキを握ったままだと車体が起きてきてしまいます。(ですからコーナーを曲がりきれないとおもってフロントブレーキを握ると余計に曲がらなくなります。私はこれで一回やらかしています。)
以上から、サーキットにおいてはコーナー進入時にはフロントブレーキを握ったまま、フロントフォークを沈ませて、キャスター角と回頭性を稼ぎ、進入後は速やかにフロントブレーキをリリースすることにより、そのままバンクさせてコーナーを曲がるということになります。

最近のスポーツバイクなどにはトラクションコントロールがついていることも多いようですが、小排気量ではまだまだ普及していません。トラクションとはタイヤが地面と接地して車を前進させようとする力のことです。「トラクションがかかってない」などといいます。コーナー進入、コーナリング中はアクセルを開けませんが、一方で、タイヤが空転せず、またより地面に食いついて安定したコーナリングを実現するために、少しだけアクセルをひねって駆動力を地面に伝えようとすることがあります。これはまさにトラクションをかけようとする行為なのです。極端な例で言えば、水や凍結などにより摩擦力の低い路面でコーナリングしようとしたとき、全くアクセルを開けることなく駆動力がゼロよりも、すこしアクセルをあけトラクションをかけたほうがより車体が安定するということになります。開けすぎるとタイヤが空転、滑ったりして加減がむずかしいのを自動で行ってくれるのがトラクションコントロールだという理解です。

  • ライテク

基本原則はスローインファーストアウト、アウト・イン・アウトです。
スローインファーストアウトとは、コーナー手前でブレーキを使用してコーナリング中にブレーキを使わなくても曲がれる速度まで減速し(スローイン)、コーナーの立ち上がりで加速し始めること(ファーストアウト)です。サーキットにおいてはアクセル全開区間を最大化することが最も重要になりますので、いくら速いコーナリングをしようとも加速区間がみじかければ意味がありません。スローインファーストアウトは行動でもサーキットでも適用できる原則です。タイヤの限界を理解しているものだけが、スローインの「スロー」を段々と速い速度域にもっていけるのです。
アウト・イン・アウトはコーナーをアウト側から進入し、コーナー中盤にイン側を経て、コーナーを抜けるときにもとのアウト側に膨らんでコーナリングすることです。
以下の画像がアウトインアウトクリッピングポイントの図です。コーナー中盤の点がクリッピングポイントになります。


クリッピングポイント - Go!KART! ゴーカートより引用

この2つは、ライダーが守るべき基本指針です。実質これだけ守っていればいい気がします。

ではここで、ライテク的コーナリングの一連の流れを書いてみます。
ステップはつま先立ちです。目線は必ずコーナーの先へ先へと送りましょう。目前の路面を見ては曲がれません。
コーナー進入前にアウト側によせて車体を立てます。腰や体を適度にイン側に持っていき重心と体重の移動を行います。そしてフロントブレーキを優しくかけ、 フロントフォークが沈み始めたらコーナリング中に余計なブレーキを使用する必要のない速度までフロントフォークを沈ませるように割り切ってフロントブレーキをかけます。進入する手前で、イン側のステップに荷重して倒し込みのきっかけを作りコーナーに進入します。コーナー入り口過ぎたところで、フロントブレーキをリリースすることでさらに必要なぶんだけバンクさせることができます。そしてコーナリング中はイン側に寄せていき、クリッピングポイントを通るあたりで車体を起こすとともにスロットルを加速し始めます。コーナーを抜けるとアウト側にいます。

  • おわりに

とりあえず、書きたかった/学んだことを羅列してみました。特に自分がすべてを実践できている/しているわけではありません。何年かたったあとこれが黒歴史に思えるほど上達してるといいなぁ。
免許取得から日が経ち、学ぶに連れ、諸元、スペックシートの意味がわかりそうな項目が増えていき、バイクライフがアンロックされいっている気分です。バイクライフがより楽しくなります。
浜松キャンパス近くの気軽に参加できそうなミニサーキットとして幸田サーキットや富士宮白糸スピードランドなどがあると思います。白糸スピードランドではプロレーサーによるライディングスクールもあります。サーキット遊びがしたい人、ぜひ一緒にサーキット走行会に行きましょう。
ライテクなんて知らなくても公道では全然問題ないはずです。ただ私はこういうことを知りたい、また知ってよかった、面白いと思いました。そう思った皆さんは、ネットや書籍、スクールなどで本物のライテクに触れる機会や場所があると思うので、ぜひ。そして私に教えてください (^_^)

ちなみにこの内容のどこがメディア・文化なのか全くわかりませんが、あえていうならここでプロレーサー&モータージャーナリスト丸山浩さんのMSTVの「モータースポーツを通して人々の生活に活力を与え文化として創造する」という標語に拠ったということで御海容くだされたく…

Waylandプロトコルデザイン: オブジェクトの寿命

原題: Wayland protocol design: object lifespan

本記事は"Wayland protocol design: object lifespan"の翻訳である。
This post is translated from the original post, "Wayland protocol design: object lifespan"
Date: 2014/8/4 07:50:42 (Tuesday) UTC

我々は今や、数年間に渡ってWaylandプロトコルを手掛けてきた。よって、私の意見をいくつか文章にしようと思い至った。
願わくば単独の投稿ではなく連投になればと思っているが、ここではいかに正しい方法でWaylandプロトコルの拡張を設計するか考察する。

最初の投稿ではプロトコルオブジェクトの寿命とそれに関係するコンポジタ/サーバーとクライアント間の競合について考える。
私は読者がすでにWaylandプロトコルの基礎について把握していることを想定してるので、そうで無い場合は
"Chapter 4. Wayland Protocol and Model of Operation"
を読むこと勧める。


如何にして、プロトコルオブジェクトが作成されるのか
(訳注: ドキュメントのWire Formatか前のブログ記事参照)
ある新しいWaylandコネクション(訳注: おそらくクライアントとコンポジタ間の接続のこと)においては、特別に作成されたるwl_displayが存在する唯一のオブジェクトである。
wl_displayは常に存在し、wl_displayを作るプロトコルは無い。

クライアントが次に作ることのできるオブジェクトがwl_registryでありwl_displayを用いる。
レジストリは全てのインターフェース(クラス)の階層の頂点である。wl_registryは数値で表される名前(name)によってグローバルオブジェクトを知らせる。
又、wl_registry.bind要求を使ってオブジェクトへ束縛するのが、プロトコルオブジェクトを作る最初で普通の方法である。

束縛することはまた少し特別である。XMLで書かれたwl_registryのプロトコル仕様書(訳注: Waylandのprotocol/wayland.xml参照)はnew_id引数型を使っているが、
新しく作るオブジェクトのインターフェース(クラス)を指定していないのである。
この特別な引数型は3つの引数へ変様する。インターフェース名(string)、インターフェースバージョン(uint32_t)、新しいオブジェクトID(uint32_t)である。
これはWaylandのコアプロトコルにおいて、唯一である。

新しいプロトコルオブジェクトを作る通常の方法はクライアントにとってはnew_id引数型を持つ要求を送ることである。
プロトコル仕様書(XML)はインターフェースが何であるかを定義する、であるからインターフェース型をワイヤープロトコルで伝える必要はない。
ワイヤープロトコルで必要なのは、新しいオブジェクトIDなのである。ほとんど全てのオブジェクトの作成がこの方法で行われる。
(訳注: ワイヤー, wireとはUNIXドメインソケットで実際に送信されるパケットやそのフォーマットに関することである)

稀ではあるが、クライアントのためにサーバーがプロトコルオブジェクトを作ることがある。
これはイベントにおいてnew_id型の引数を持つことによって起きる。
クライアントがこのイベントを受信をする度に新しいプロトコルオブジェクトを受け取ることとなる。

全てのリクエストやイベントは(クラスのメンバーの様に)何かのインターフェースの一部であるから、このことがインターフェースの階層を作り出している。
例えば、wl_compositorオブジェクトはwl_registryから作られ、wl_surfaceオブジェクトはwl_compositorから作られる。

オブジェクトの作成は絶対に失敗しない。一度リクエストやイベントが送信されれば、それが作った新しいオブジェクトが存在する、以上。
このことがプロトコルを非同期に保っており、それ故、オブジェクト作成が成功したかどうかを返信したり確認したりする必要が無い。


如何にして、プロトコルオブジェクトは破棄されるのか
プロトコルオブジェクトを破棄する方法は2通りある。最も一般的な方法はデストラクタとして指定されたリクエストをインターフェース内に持つことである。
ほとんどの場合、このリクエストは"destroy"と呼ばれる。
クライアントのコードがwl_foobar_destroy()関数を呼ぶ時、そのリクエストはサーバーに送られ、そのオブジェクトのクライアント側のプロキシ(struct wl_proxy)が破棄される。
その後、サーバーは将来のいずれかの時点において破棄要求を処理する。

もう一つの方法はイベントによってオブジェクトを破棄することである。
そのような場合では、インターフェースのプロトコル仕様書でデストラクターを定義する必要は無い。
又、自動化や安全装置の役割を果たすものが無いので、そのイベントが破壊的であることをドキュメントに明記しなければならない。
これはオブジェクトが何時死ぬかをサーバーが決める場合のものであり、全ての状況において正しく動くよう、プロトコル設計に細心の注意を払わなければならない。
クライアントがこの様なイベントを受け取った時できることはプロキシオブジェクトを破棄することだけである。この様なインターフェースの(非)有名な例がwl_callbackである。


ブギーマンに入る: 競合
クライアントとサーバーの双方がどのプロトコルオブジェクトが存在するかに同意することは非常に重要である。
もしクライアントが、サーバーの考えにないオブジェクト、若しくは引数としての参照のリクエストを送信した場合、サーバーはプロトコルエラーを起こし、クライントとの接続を切断する。
明らかにこれは絶対に起きるべきでは無く、サーバーがクライアントが破棄したオブジェクトへのイベントを送信することも又、同様である。

Waylandは完全な非同期プロトコルであるので、私達には明確な保証が無い。
サーバーはクライアントがオブジェクトを破棄するのと同時にイベントを送るかもしれず、その時イベントはクライアントがもう知らないオブジェクトを対象としているのである。
クライアントが自滅するのではなく(これはサーバーの仕事である)、libwayland-clientにはうまいやり方が存在する:
それは暗黙的にサーバーがそのオブジェクトが本当に亡くなったことを確認するまで、破棄されたオブジェクトへのイベントを無視するのである。

これはデストラクターがリクエストである様なインターフェースにとっては非常にうまく行く。
もしクライアントが最初に破棄要求を送信し破棄されたオブジェクトのリクエストを送信した場合、そのオブジェクトが自分で頭を撃ちぬくのである。如何なる競合も必要で無い。

他の場合、デストラクターイベントにおいては物事はややこしくなる。
サーバーはクライアントがオブジェクトのリクエストを送っているのと同じ時に同じオブジェクトへのデストラクターイベントを送信するかもしれない。
サーバーが最終的にリクエストを取得した時、オブジェクトはすでに破棄されている。
それ故、デストラクターイベントを安全に使うほとんど唯一の方法はインターフェースが如何なるリクエストも持たないことなのである。
常にそうであり、将来の拡張においさえもそうである。
更に、そのインターフェースのオブジェクトは如何なる箇所においても引数として使われるべきでは無く、さもなくば、競合に遭遇することとなるであろう。
これがデストラクターイベントを正しく使うのが難しい理由である。


ギーマンの兄弟
オブジェクト(例えばサーバーが作るオブジェクト)を作るイベントに関するもう一つのたちの悪い競合がある。
もしサーバーがある新しい(子)オブジェクトを作成しながら、その(親)オブジェクトのイベントを送信している最中に、
クライアントがその(親)オブジェクトを破棄していていたなら、サーバーはクライアントが実際にイベントを処理したかどうか知り得ない。
もし、クライアントがそのイベントを無視したなら、クライアントは決してその新しいオブジェクトを破棄するようサーバーには伝えないであろう、そしてサーバーでリークすることになる。

その(親)オブジェクトが破棄されたら、その子オブジェクトを全て破棄するようなプロトコル仕様書を書くことによってこの落とし穴を回避することができる。
しかし、そうなれば、クライアントはその親オブジェクトを破棄した後、その子オブジェクトのデストラクターリクエストを送ることができない。
これは、サーバーが知らないオブジェクトのリクエストを見ることになるからである。
もし子のインターフェースがデストラクターを定義するなら、クライアントは親オブジェクトを破棄した後、その(子オブジェクトの)プロキシを破棄することができない。
もし子のインターフェースがデストラクターを定義しないなら、親オブジェクトが破棄されるまでサーバー側の資源を決して開放できない。

クライアントは全ての子オブジェクトをある定義されたコンストラクターでまとめて破棄し、その後直ちに親オブジェクトを破棄することもできる。
私はこれが動くかどうかわからないが、たぶん動くであろう。もし動かないなら、全体の破棄するプロトコルの順序を定める必要がある。
クライアントがサーバーにその親オブジェクトを破棄したいと伝え、サーバーはそれに応答しそのオブジェクトのイベントを以後送信しないことを保証する。
その後、クライアントが実際に親オブジェクトを破棄する。一周して、単に美しい非同期プロトコルから同期プロトコルにしただけである、おめでとう。


まとめと提案
以下がWaylandプロトコルの拡張を設計する時の私の提案である。

  • 常に全てのオブジェクトを破棄すると保証された方法を用意する

これは自明に思えるが、サーバーとクライアントの両方の資源が開放できるようなオブジェクトを破棄する方法が無い、Waylandコアプロトコルの問題をいくつか修正してきた。
更に、未だいくつか問題が修正されていない。

あなたの新しいインターフェースがデストラクタリクエストを必要とするのではないかという疑問が少しでもあるなら、デストラクタリクエストを加えておきなさい。
普通のリクエストより後から追加するのが厄介である。
もしデストラクタリクエストがないなら、クライアントはサーバーにこれらのオブジェクトの資源を開放するよう伝えることができない。

  • デストラクターイベントを使用しない

正しく設計するのは難しく、後からインターフェースを拡張するのは良くないであろう。
クライアントはサーバーに資源を開放するよう伝えることができないので、デストラクターイベントを有するオブジェクトは短命でなければならない。
又、その破棄は必ず保証されなければならない。

  • 深い考えなしにサーバー側で作られるオブジェクトを使用しない。

リークしたり爆発したりしない様な破棄の順序を設計するのは難しい。

Waylandプログラミング入門

前回に引き続きX Window Systemの後継、Waylandに関する記事です。
今回は具体的にWaylandのクライアント側で動くWaylandクライアントが実際にどのように動くのかを、筆者が書いた単純なプログラムから説明しようと思います。

前回も書きましたが、Waylandのプロトコルオブジェクト指向の非同期なサーバとクライアント間でのIPCです。
Waylandプロトコルのリファレンス実装であるlibwayland-clientとlibwayland-serverはCで書かれているので、以下ではC言語でプログラムを記述します。

以下に今回の対象であるsimple-clientのソースを示します。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <wayland-client.h>
#include <wayland-client-protocol.h>
#include "os-compatibility.h"

struct simple_client {
    struct wl_display       *display;
    struct wl_registry      *registry;
    struct wl_compositor    *compositor;
    struct wl_buffer        *buffer;
    struct wl_surface       *surface;
    struct wl_shm           *shm;
    struct wl_shell         *shell;
    struct wl_shell_surface *shell_surface;
    void *data;
    int width, height;
};

void die(const char msg[])
{
    fprintf(stderr, "%s", msg);
    exit(EXIT_FAILURE);
}

static void handle_ping(void *data, struct wl_shell_surface *shell_surface, uint32_t serial)
{
    wl_shell_surface_pong(shell_surface, serial);
}

static void registry_handle_global(
    void *data, struct wl_registry *registry, uint32_t name,
    const char *interface, uint32_t version)
{
    struct simple_client *client = data;
    printf("interface=%s name=%0x version=%d\n", interface, name, version);
    if (strcmp(interface, "wl_compositor") == 0)
        client->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1);
    else if (strcmp(interface, "wl_shell") == 0)
        client->shell = wl_registry_bind(registry, name, &wl_shell_interface, 1);
    else if (strcmp(interface, "wl_shm") == 0)
        client->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
}

static void create_shm_buffer(struct simple_client *client)
{
    struct wl_shm_pool *pool;
    int fd, size, stride;

    stride = client->width * 4;
    size = stride * client->height;

    fd = os_create_anonymous_file(size);
    if (fd < 0) {
        fprintf(stderr, "creating a buffer file for %d B failed: %m\n", size);
        exit(1);
    }

    client->data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (client->data == MAP_FAILED) {
        fprintf(stderr, "mmap failed: %m\n");
        close(fd);
        exit(1);
    }

    pool = wl_shm_create_pool(client->shm, fd, size);
    client->buffer =
        wl_shm_pool_create_buffer(pool, 0,
            client->width, client->height ,stride, WL_SHM_FORMAT_ARGB8888);
    wl_shm_pool_destroy(pool);

    close(fd);
}

void draw_argb8888(void *d, uint8_t a, uint8_t r, uint8_t g, uint8_t b, size_t count)
{
    while (count-- > 0)
        *((uint32_t *)d + count) = ((a << 24) | (r << 16) | (g << 8) | b);
}

struct simple_client *simple_client_create()
{
    static struct wl_registry_listener registry_listener = {
        registry_handle_global, NULL
    };
    struct simple_client *client = malloc(sizeof (struct simple_client));
    if (!client)
        die("Cannot allocate memory for simple_client\n");

    client->display = wl_display_connect(NULL);
    if (!client->display)
        die("Cannot connect to Wayland display\n");

    client->registry = wl_display_get_registry(client->display);
    if (!client->registry)
        die("Cannot get registry from Wayland display\n");
    wl_registry_add_listener(client->registry, &registry_listener, client);

    wl_display_roundtrip(client->display);
    wl_display_dispatch(client->display);

    client->width = 600;
    client->height = 500;
    client->surface = wl_compositor_create_surface(client->compositor);
    client->shell_surface = wl_shell_get_shell_surface(client->shell, client->surface);

    create_shm_buffer(client);

    if (client->shell_surface) {
        static const struct wl_shell_surface_listener shell_surface_listener = {
            handle_ping, NULL, NULL
        };
        wl_shell_surface_add_listener(
            client->shell_surface, &shell_surface_listener, client);
        wl_shell_surface_set_toplevel(client->shell_surface);
    }

    wl_surface_set_user_data(client->surface, client);
    wl_shell_surface_set_title(client->shell_surface, "simple-client");

    draw_argb8888(client->data, 0x00, 0x00, 0x00, 0xff, client->width * client->height);
    wl_surface_attach(client->surface, client->buffer, 0, 0);
    wl_surface_damage(client->surface, 0, 0, client->width, client->height);
    wl_surface_commit(client->surface);

    return client;
}

int main(int argc, char **argv)
{
    struct simple_client *client = simple_client_create();
    while (wl_display_dispatch(client->display) != -1);
    free(client);
    exit(EXIT_SUCCESS);
}

このプログラムは透明がかった青色の600x500の長方形を画面に表示するだけのプログラムです。
処理がこれだけなのに行数が130行ぐらいあるのはクライアント側ですべて処理するためです。
下画像はこのプログラムの実行結果です。

WaylandのアーキテクチャAPIを記した公式ドキュメントがここにあるので適宜参照してください

[解説]

処理の中心はsimple_client_create()です。ここでstruct simple_clientを初期化、設定して最後にmainの最後でループして終了です。
大まかな流れは下のような感じです:

  1. Waylandコンポジタに接続
  2. Waylandコンポジタ上のグローバルオブジェクトを取得
  3. 描画用の共有メモリのプールを作成
  4. プールから画面に表示するsurfaceのバッファを切り出す
  5. バッファに表示内容、具体的なピクセルデータを書き込む
  6. surfaceにバッファをくっつけて、Waylandコンポジタに表示するよう要求
  7. ループでWaylandコンポジタからのイベント読み続ける。

WaylandプロトコルではWaylandコンポジタへの要求はコンポジタ上のオブジェクトのメソッドの呼び出しとなります。(Request)
またそのコンポジタ上のオブジェクトから通知や、指定したコールバックの呼び出し等が起こります。 (Event)

wl_displayはWaylandコンポジタとクライアントが通信するときに必ず必要になるコアオブジェクトです。
wl_registryからはコンポジタが保持しているオブジェクトを取得できます。
つまり、wl_display_connect()でコンポジタとの通信を確立したあと、そのコアオブジェクトからwl_registryを得て、コンポジタ上のグローバルオブジェクト(e.g. wl_shell, wl_shm)を取得しstrut simple_clientを初期化します。
この際の流れがこれからも重要になるので詳しく解説します。
wl_registry_add_listener()はつまり、サーバ上のwl_rergistry(つまり今取得したregistry)のadd_listenerメソッドを呼び出すわけです。
add_listener()メソッド呼び出し後は、コンポジタからの返答(Event)が帰ってきます。これがwl_registry_listener::globalで、こいつでコンポジタ内のグローバルオブジェクトが通知されます。プログラムではregistry_handle_global()に設定しています。
Waylandではwl_OBJECTNAMEのEventを受け付けるwl_OBJECTNAME_listenerが存在し、wl_OBJECTNAME_*()の命名規則に従った関数でRequestを送ったり、なにか変化があった場合、Eventとしてwl_OBJECTNAME_listenerの関数が呼ばれます。wl_shell_surface_listener::pingではプログラムが応答可能かカーソルがsurfaceの領域に入った時呼ばれるので、wl_shell_surfae_pong()で応答します。
Eventはwl_displayにキューされるので、このキュー内にあるEventを全て処理するのがwl_display_dispatch()、wl_display_roundtrip()は送信したRequestがすべてコンポジタで処理されるまでブロックします。

wl_compositorはコンポジタを表しており、ここから実際に表示するsurfaceを取得します。
wl_shellは表示するsurfaceに対しマウスカーソル、リサイズ、回転等のデスクトップアプリケーション用に(最低限)必要なインターフェイスを実装するためのオブジェクトです。
wl_shell_get_shell_surface()でsurfaceからwl_shell用のshell_surfaceを取得します。
そのあとdraw_argb8888()で600x500の長方形に対し透過した青色の長方形を描画します。
そのあと、surfaceに描画したbufferを関連付け、変化した範囲(今回は全域)を指定し、コミットします。
最後はwl_display_dispatch()でrequestを送信し、コンポジタからのイベントを待ち続けます。
ここで、ついに描画した長方形が画面に表示されます。

どうでしょうか。わかりづらい文章になってしまいましたが細かいことは大体ドキュメントに書いてあります。
Westonのclients/にあるクライアントプログラムはWestonで試験的に導入されていてWaylandのコアプロトコルにはまだ入っていないプロトコルやclients/以下にあるtoytoolkitと呼ばれる簡易ライブラリに依存しているものが多くあり、純粋なWaylandのプロトコルだけで動くのは、simple-shmやsimple-eglぐらいです。
Waylandは必要最低限のAPIしか用意しませんが、今でもxdg_shellやsubsurface等の新しい便利な機能が追加されようとしています。

もしWaylandに興味のある方は、GTKやEFL等が移植されているので試してみてはどうでしょうか。大体実行時にバックエンドの切り替えができるようになっていると思うので、コンパイルし直す必要がないことがほとんどでしょう。

Wayland/Westonについて

最近、WaylandとかWestonとかよく見聞きします。(主にPhoronixとかで...)
で、何故か興味を持ってしまったので、近頃はWaylandがどんなものか見極めんと色々やっとったわけです。

Waylandがなぜ必要なのか、X11とWaylandの歴史を追いながら比較している素晴らしい発表がLinux.conf.au 2013にあったのでまず紹介したいと思います。

もしくは

上記を見てもらえればWaylandができた理由とその意義が大体お分かり頂けるのではないかと思います。

発表の中でもありましたが、Waylandの開発者はXの開発者でもあります。複雑怪奇になったXを改善するためテーマやフォントの描画の処理をどんどんクライアント側に移していきます。
Window Managementをするのが難しくなってきたのでX serverとは別にWindow Manager(WM)にすべて描画させます。
ではWMとクライアント側にどんどん処理を移動させた結果、X serverは現在何をしているのでしょうか。
実はほとんど何もしていなくて、WMとクライアントの間でデータを受け渡ししているだけになってしまったのです。
間にいるX serverいらんやん?となってしまいます。発表ではreally bad IPCと揶揄されています。
Waylandではクライアントが「これを表示してくれ」といったら、サーバ (Waylandコンポジタ)が表示する。これで完了です。

Waylandはプロトコルの名前で、ライブラリとしては非同期のオブジェクト指向型のライブラリlibwayland-clientとlibwayland-serverから成ります。
ライブラリとしてのWaylandはWaylandクライアントとWaylandコンポジタとの通信(IPC)が主な仕事です。
実際に画面に表示する内容はクライアント側で描画します。ですからクライアントはどのように描画しても構いません。
描画したあとは、その内容を表示するよう要請します。クライアント側に委ねられるのは描画だけでは無く、リサイズやキー入力等も非同期で飛んで来るので、全て自分で処理します。
Waylandコンポジタはそれぞれのクライアントが描画した内容を処理して、正しく画面に表示します。

WestonはWaylandコンポジタのリファレンス実装です。元来、KMSやOpenGL ESのような現代的な環境で動くことを目標にしていますが、コンポジタがどのように画面に表示するかはコンポジタ次第なので、例えばフレームバッファ(fbdev)や、X11、Remote Desktop Protocol (RDP)の上で動くバックエンドも用意されています。

最近はGTKやQt、EFL(Enlightenment Foundation Library)、SDL、Clutter等がWayland対応を完了、もしくは進めています。
純粋に、Xに依存したライブラリを利用していない、GTKやEFLクライアントは既にWestonの上で動作します。またGNOMEのmutterもWaylandで動作し、GNOME 3.10から実験的なWaylandサポートが実装され、先日リリースされたGNOME 3.12ではより改善されています。
最近のGNOMEのWayland対応についてはWayland in 3.12, and beyond - Clean Rinseあたりが詳しいかと。

次はWaylandプログラミングについて書くかもしれません。

planetfsで通信

planetfsは自分が作っているネットワーク透過性、ネットワークのファイル抽象化を目的としたファイルシステムです。
Github: pfpacket / fuse_planetfs

当初の目標を一応のところ果たしたのでどんな感じになったか書いてみる。

ここLinusLinuxの音声ファイルがあります。
これをローカルからリモートで再生したい場合、普通であればネットワーク用のコマンドを用いなければなりませんが、planetfsを使うと特別なツールを使わずにLinuxの基本的なコマンドだけでできます。

TCPの8000番ポートで通信する場合:
リモート側:

touch /net/tcp/\*\!8000
cat /net/tcp/\*\!8000 > /dev/audio

ローカル側:

cat english.au > /net/tcp/127.0.0.1\!8000

これでリモート側で音声が流れます。

ちょっと説明
リモート側の

touch /net/tcp/\*\!8000

でまずTCPの8000番ポートにサーバーを立てます。(touchコマンドがファイルをopen(2)するのでCtrl-C)
次にcat コマンドで先ほど作成したサーバーファイルを開くことでlisten状態になり、相手がconnect(2)してきた時点で、catコマンドが発行したopen(2)が返ります。あとはcatコマンドがファイルからread(2)して/dev/audioにwrite(2)するので受けとったファイルが/dev/audioに流れて、音声が再生されます。

ローカル側では/net/tcp/接続先IPアドレス!ポート番号 をopen(2)することで相手先に接続出来ます。
catコマンドでLinusの発音ファイルenglish.auを/net/tcp/127.0.0.1\!8000にリダイレクトすることで127.0.0.1:8000番地に接続し、音声ファイルを送った後catコマンドが終了します。
ちなみに/net/tcp/127.0.0.1\!8000をrmしないとリモート側では音声が流れた後もcatコマンドが終了しません。ローカル側で接続ファイルをrmすることでコネクションが切断されます。

次はTCPのコネクション毎にディレクトリを作成しようなどと考えています。

Plan9を理想としたネットワークのファイル抽象化ファイルシステム

Plan9を知ってからリソースをファイルで抽象化するという概念をネットワークにも適用するという考えがLinuxでも実現出来ればと思っていました。
Linuxでどうやったらそれっぽく実装できるか考えた結果、ファイルシステムを作ってプロセスのopen,read,write,closeといったファイルへのシステムコールをフックするような感じでやればいけると思い、簡単ではありますが実装しました。

Github: pfpacket / fuse_planetfs

ファイルシステムを作ると言ってもLinuxカーネルVFSに自分でゴニョゴニョするには、自分の知識が足りなさそうだったのでFUSE(Filesystem in Userspace)を使いました。

どのようにネットワークをファイルで抽象化するかはPlan9Extending UNIX File Abstraction for General-Purpose Networking (pdf)を参考にしました。

つまりネットワークをファイルシステム名前空間マッピングすることで、プロセスからopen,read,writeだけでアクセスできるようにします。

例えばこのファイルシステムを/netにマウントした場合、単純なHTTPクライアントはCで下のようになります。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
    int fd, size;
    char buffer[65535] = {0};
    static char const *request = 
                "GET /index.html HTTP/1.1\r\n"
                "Host: www.google.co.jp\r\n"
                "Connection: close\r\n\r\n";

    fd = open("/net/eth/ip/tcp/74.125.235.240:80", O_CREAT | O_RDWR, S_IRWXU);
    if (fd < 0) {
        perror("open");
        return EXIT_FAILURE;
    }

    /* Send a HTTP request */
    size = write(fd, request, strlen(request));
    if (size < 0) {
        perror("write");
        return EXIT_FAILURE;
    }

    /* Receive the response of the server */
    do {
        size = read(fd, buffer, sizeof (buffer));
        if (size < 0) {
            perror("read");
            break;
        }
        /* Display it */
        write(STDOUT_FILENO, buffer, size);
    } while (size != 0);

    close(fd);
    return EXIT_SUCCESS;
}

また単純なTCPサーバーは下のようになります。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

void die(char const *prefix)
{
    perror(prefix);
    exit(EXIT_FAILURE);
}

int main(int argc, char **argv)
{
    int fd, client_fd, size;
    char client_path[1024] = {0}, buffer[65535] = {0};

    /* Establish a server waiting on port 10000 */
    fd = open("/net/eth/ip/tcp/*:10000", O_CREAT | O_RDWR, S_IRWXU);
    if (fd < 0)
        die("open");

    while (1) {
        /* Accept a client's connection */
        size = read(fd, client_path, sizeof (client_path));
        if (size < 0)
            die("read");
        printf("accepted client path: %s\n", client_path);

        /* Open a connection to the client */
        client_fd = open(client_path, O_RDWR);
        if (client_fd < 0)
            die("open");

        /* Receive the response of the remote host */
        do {
            size = read(client_fd, buffer, sizeof (buffer));
            if (size < 0) {
                perror("read");
                break;
            }
            /* Display it */
            write(STDOUT_FILENO, buffer, size);
        } while (size != 0);
        close(client_fd);
    }
    close(fd);
    return EXIT_SUCCESS;
}

名前解決もファイル抽象化されています。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
    int fd, size;
    char buffer[1024];
    static char const *request = "resolve_inet www.google.co.jp";

    fd = open("/net/dns", O_RDWR, S_IRWXU);
    if (fd < 0) {
        perror("open");
        return EXIT_FAILURE;
    }

    /* 
     * Request DNS service. format:
     * 'resolve hostname'       - AF_UNSPEC
     * 'resolve_inet hostname'  - AF_INET
     * 'resolve_inet6 hostname' - AF_INET6
     */
    size = write(fd, request, strlen(request));
    if (size < 0) {
        perror("write");
        return EXIT_FAILURE;
    }

    /* Read /net/dns to get the result */
    for (;;) {
        size = read(fd, buffer, sizeof (buffer));
        if (size <= 0)  // read error or EOF
            break;
        /* Display it */
        printf("%s\n", buffer);
    }

    close(fd);
    return EXIT_SUCCESS;
}

こんな感じでTCPサーバーやパケットキャプチャなどに対応しています。
コード自体はかなり粗削りで、もっと簡単にファイルへの操作を定義、追加できるよう作業中です。
ちなみに作り始めて半分くらい作ってからExtending UNIX File Abstraction for General-Purpose Networkingを知ったのですが、この中ではカーネルモジュールからprocfsで/proc/netfsを作るとことになっています。そっちの方が簡単だと思います。FUSEだとファイルシステムの中を全部作らなければなりません。

(zsh) rebuild auto-complete index

Stack Exchange - rebuild auto-complete index (or whatever it's called)

zshを実行中に新しいソフトウェアをインストールするとなぜかその新しくインストールされたコマンドがタブ補完されない問題。
結構長い間放置してたので解決策をググって上記を発見。

どうやら実行ファイルのキャッシュを再生成するにはコマンドを用いるなら:

$ rehash
$ hash -rf

のどちらか。

またhash_list_allオプションが設定されていないのを確認して
.zshrcに以下を記述しておけばzshはタブ補完する際に自動的にキャッシュを再生成してくれる。

zstyle ":completion:*:commands" rehash 1