読者です 読者をやめる 読者になる 読者になる

X11 extensionsのXRandRでディスプレイの名前と使用できる解像度とリフレッシュレートを取得してみる

man見たりしたけど、XRRScreenResourcesとかが見当たらなかったのでX11/extension/Xrandr.hの転載をのせつつメモ。
XRandRのバージョンが1.2以上なら使えるはずです。

準備

以下のヘッダをインクルードします。

#include <X11/extension/Xrandr.h>

まずXRRGetScreenResourcesを使って、XRRScreenResourcesのポインタを取得します。XRRScreenResoucesのoutputsはディスプレイの数だけ要素があるRROutputの配列で、要素数はnoutputに格納されています。RROutputはXIDのtypedefです。使い終わったらXRRFreeScreenResourcesで解放しましょう。

typedef struct _XRRScreenResources {
    Time	timestamp;
    Time	configTimestamp;
    int		ncrtc;
    RRCrtc	*crtcs;
    int		noutput;
    RROutput	*outputs;
    int		nmode;
    XRRModeInfo	*modes;
} XRRScreenResources;

XRRScreenResources * XRRGetScreenResources (Display *dpy, Window window);
void XRRFreeScreenResources (XRRScreenResources *resources);

名前の取得

ディスプレイの名前を取得するにはXRRGetOutputInfoを使ってXRROutputInfoのポインタを取得します。XRROutputInfoのnameに名前の文字列、nameLenにその文字列の長さが格納されています。使い終わったらXRRFreeOutputInfoで解放しましょう。

typedef struct _XRROutputInfo {
    Time	    timestamp;
    RRCrtc	    crtc;
    char	    *name;
    int		    nameLen;
    unsigned long   mm_width;
    unsigned long   mm_height;
    Connection	    connection;
    SubpixelOrder   subpixel_order;
    int		    ncrtc;
    RRCrtc	    *crtcs;
    int		    nclone;
    RROutput	    *clones;
    int		    nmode;
    int		    npreferred;
    RRMode	    *modes;
} XRROutputInfo;

XRROutputInfo * XRRGetOutputInfo (Display *dpy, XRRScreenResources *resources, RROutput output);
void XRRFreeOutputInfo (XRROutputInfo *outputInfo);

解像度とリフレッシュレートの取得

XRRScreenResourcesのメンバにmodesというのがあって、XRRModeInfoの配列になっています。その要素数はXRRScreenResourcesのnmodeです。解像度はXRRModeInfoのwidthとheightに格納されています。

typedef unsigned long XRRModeFlags;

typedef struct _XRRModeInfo {
    RRMode		id;
    unsigned int	width;
    unsigned int	height;
    unsigned long	dotClock;
    unsigned int	hSyncStart;
    unsigned int	hSyncEnd;
    unsigned int	hTotal;
    unsigned int	hSkew;
    unsigned int	vSyncStart;
    unsigned int	vSyncEnd;
    unsigned int	vTotal;
    char		*name;
    unsigned int	nameLength;
    XRRModeFlags	modeFlags;
} XRRModeInfo;

リフレッシュレートはXRRModeInfoを使って求める必要があり、xrandr-1.4のソースの540行目以降からパクったを参考にした実装例は以下のようになります。一応C++です。

double mode_refresh(XRRModeInfo const& info)
{
    auto v_total = info.vTotal;
    if( info.modeFlags & RR_DoubleScan ) {
        v_total *= 2;
    }
    if( info.modeFlags & RR_Interlace ) {
        v_total /= 2;
    }

    return info.hTotal && v_total
        ? static_cast< double >( info.dotClock ) / ( static_cast< double >( info.hTotal ) * v_total )
        : 0;
}

実装例で一応C++です。本当はXRRQueryVersionなどを使ってXRandRを使っているかどうかやバージョンを調べる必要とかありますが面倒なので省略。mode_refreshは上の方を見てください。

int main()
{
    Display* disp = XOpenDisplay( nullptr );

    auto res = XRRGetScreenResources( disp, XRootWindow( disp, DefaultScreen( disp ) ) );
    for( int i = 0; i < res->noutput; ++i ) {
        // 名前の表示
        auto info = XRRGetOutputInfo( disp, res, res->outputs[i] );
        std::cout << std::string( info->name, info->nameLen ) << " : " << std::endl;
        // 使える解像度とリフレッシュレートを列挙
        for( int j = 0; j < res->nmode; ++j ) {
            std::cout << "  " 
                << res->modes[j].width << "x" << res->modes[j].height 
                << "-" << mode_refresh( res->modes[j] )
                << std::endl;
        }
        XRRFreeOutputInfo( info );
    }
    XRRFreeScreenResources( res );

    XCloseDisplay( disp );
}