« ネクソンジャパンの社長さん | トップページ | Linux »

2008年1月 2日 (水)

X11で画面の解像度を変える

昔はXF86 Video Mode Extensionを使っていたのだが、これは最近のX.orgではうまく機能しなくなってしまったようだ。(互換のためにAPIは実装されているようだが、実質的に機能しない。)

現代的には、XRandR Extensionというものを使う。しかし、XRandRは、資料があまりない。その上、どういうわけか、関数とかデータ型とかの名前がめちゃめちゃだし、関数の仕様にも首をひねる部分が多く、理解を妨げる。

例えば、XRandRの基本のデータ構造は、XRRGetScreenInfo() という関数を呼ぶと得られるのだが、ここで得られるデータ構造の型名はXRRScreenConfigurationであって、XRRScreenInfoという名前のデータ構造は存在しない。XRRGetScreenInfo() は、動的に割り当てたXRRScreenConfigurationへのポインタを返すのだが、使い終わったらこのメモリをアプリ側で開放する必要がある。で、XRRScreenConfigurationを開放するための関数はXRRFreeScreenConfigInfo()というのだ。XRRFreeScreenInfoでも、XRRFreeScreenConfigurationでもなく。

誰だ、こういう名前つけたの。責任者出て来い、みたいな。

ということで、使い方のまとめ。(SL Viewerで使うものだけ。)

#include <X11/extensions/Xrandr.h>
XRandRを使うときには、X11の標準のヘッダに加えて、これをインクルードする。大文字小文字は、ここに書いてある通りにする必要がある (ファイルシステムにもよるが)。
Status XRRQueryVersion(Display *dpy, int *major, int *minor)
とにかく最初に呼ぶ。XRandR Extension がサポートされていれば戻り値は非0になる。サポートされていなければ0。majorとminorには、アプリ側で確保したint型の変数 (初期化不要) のアドレスをわたす。ここに、XRandRのバージョンのメジャーとマイナーが入って戻ってくる。普通は、majorが指す変数の値が1ならOK。minorは、使う関数によっては値をチェックする必要がある (1以上とか2以上とか) ようだが、以下の関数はバージョン1.0で使えるのでチェック不要。
XRRScreenConfiguration *XRRGetScreenInfo(Display *dpy, Drawable draw)
この関数を呼ぶと、XRRScreenCOnfiguration型のデータへのポインタが得られる。drawには、ディスプレイdpy配下にあるスクリーンのうち、解像度を変更しようとしているスクリーンのルートウィンドウを指定する。(スクリーン番号などではなく。なぜ、こんな仕様なのか不明。) 以下の関数の引数にconfigというパラメタがあったら、そこには、この関数の結果として得られたポインタをわたす。使い終わったら、XRRFreeScreenConfigInfoで開放する。
void XRRFreeScreenConfigInfo(XRRScreenConfiguration *config)
XRRGetScreenInfoで得たXRRScreenConfigurationを開放する。
Time XRRConfigTimes(XRRScreenConfiguration *config, Time *timestamp)
この関数は、XRRGetScreenInfoを使ってXRRScreenConfigurationを得た時刻を調べるのに使う。Time型が、引数 (ポインタ) と戻り値の両方に登場するが、両者に同じ値が返るらしい。時刻なんかどうでもいいと思うかもしれないが、この時刻は、あとでXRRSetScreenConfig()を呼ぶときに必要になる。
XRRScreenSize *XRRConfigSizes(XRRScreenConfiguration *config, int *nsizes)
利用可能な解像度の一覧を得る。関数の結果はXRRScreenSizeへのポインタという型だが、実際には、XRRScreenSizeを要素型とする配列の先頭のポインタが得られる。アプリ側でint型の変数を用意 (初期化不要) して、そのアドレスをnsizesとしてわたすと、結果として得られる配列の大きさ (要素数) がセットされる。XRRScreenSizeというのはstructで、ここにwidthとheightというメンバがあって、それぞれ横方向、縦方向の解像度 (ドット数) を表している。
Rotation XRRConfigRotations(XRRScreenConfiguration *config, Rotation *rotation)
この関数は、現在の画面の回転状態 (90度回転させて縦長表示にできるようなもの) を得る。関数名はconfig rotations だが、configure するわけではないし、得られるのは回転状態を表す1個の値だ。引数でRotation型の変数のアドレスをわたすと、その変数に値がセットされると同時に、同じ値が戻り値としても返る。なお、サポートされている回転状態の一覧を調べる機能はないようだ。Xrandr.hに理論的に可能なすべての状態がマクロとして定義されているが、当然使えるものはハードによって異なるはず。どれが実際に使えるかを知るためには、実行時に試してみるしかないようだ。
Status XRRSetScreenConfig(Display *dpy, XRRScreenConfiguration *config, Drawable draw, int size_index, Rotation rotation, Time timestamp)
この関数で実際に解像度を変更する。XRRGetScreenInfo()と同じく、drawにはルートウィンドウを指定する。size_indexには、変更後の解像度を、XRRConfigSizes()の戻り値として得られた配列の添え字番号で指定する。rotationには、XRRConfigRotations()で得られた値をそのまま指定する (勝手な値を指定するとエラーになる模様)。timestampにはやはりXRRConfigTimes()で得られた値をそのまま指定する。この関数の戻り値はStatus型なのだが、値0が成功で、それ以外がエラー。
なお、この関数は、XRRGetScreenInfo()を呼んでからすぐに呼ばないといけないらしく、時間が経つとエラーになってしまうそうだ。そのためにtimestampを渡すのだ、とも言われている。そして、時間が経った場合は、(いったんメモリを開放したあと) XRRGetScreenInfo() からやり直す、という記述も見かける。しかし、関数が非0を返したときに、そのエラーの原因が、時間が経ったためなのか、他の理由なのかを区別する方法が不明。いったい、なんなんだ、この仕様は。

« ネクソンジャパンの社長さん | トップページ | Linux »

コメント

XRandRの資料がないと愚痴を書いたところ、詳しい資料のありかを教えていただきました。(URLがめちゃめちゃ長くて表示は途中で切れてます。)

http://gitweb.freedesktop.org/?p=users/keithp/randrproto.git;a=blob;h=f0ec5e1326565e7008056159249df2329eab1156;hb=61d60a93dc0c827ef970f21f6b80099cc6c958f1;f=randrproto.txt

だそうです。

あのー、これって、APIというか、その下で流れているプロトコルの資料じゃないですか。しかも、このURLはgitレポジトリ。でもって、users/keithpの下ですか? つまり、これは、XRandRの開発の中心人物であるKeith Packardさんの、個人的なメモなわけですね…。ま、いいけど。

今度、暇なときにゆっくり読みます。(って、今が暇なときなんですけどね。)

おしえていただいた資料を読んだところ、いろいろと間違いに気づき、いくつかの謎も解決し、そして、このエントリに書いたインターフェースはすでに時代後れ (obsolete) だから使わない方がいい、ということまでわかりました…。

しょうがないんで、今から書き直します。Linux用。

コメントを書く

(ウェブ上には掲載しません)

トラックバック

この記事のトラックバックURL:
http://app.f.cocolog-nifty.com/t/trackback/285638/9690508

この記事へのトラックバック一覧です: X11で画面の解像度を変える:

« ネクソンジャパンの社長さん | トップページ | Linux »