X11: ビジュアル

ビジュアル
ウィンドウを作成する前に、ビジュアルについて説明します。

ハードウェアによっては、複数の方法で色を扱うことができる場合があるため、それぞれのカラーの扱い方は「ビジュアル」として定義されています。

各スクリーンにおいて、複数のビジュアルがサポートされています。

主にウィンドウで使用されるデフォルトのビジュアルは、各スクリーンであらかじめ定義されており、DefaultVisual(disp) マクロや DefaultVisualOfScreen(screen) マクロなどで取得できます。
返される値は、Visual 構造体のポインタです。
プログラム (1)
指定スクリーンでサポートされているビジュアルのリストを取得したい場合は、XGetVisualInfo 関数を使います。
この関数は X11/Xutil.h で定義されているので、インクルードファイルを追加してください。

<03a-visual.c>
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

const char *class_names[] = {
 "StaticGray", "GrayScale", "StaticColor", "PseudoColor", "TrueColor", "DirectColor"
};

int main(int argc,char **argv)
{
    Display *d;
    XVisualInfo *vi,vitmp;
    int i,cnt;
    
    d = XOpenDisplay(NULL);
    if(!d) return 1;

    vitmp.screen = DefaultScreen(d);

    vi = XGetVisualInfo(d, VisualScreenMask, &vitmp, &cnt);
    if(vi)
    {
        for(i = 0; i < cnt; i++)
        {
            printf("----[%d]---\n", i);
            printf("visualid: %lu\n", vi[i].visualid);
            printf("depth: %d\n", vi[i].depth);
            printf("class: (%d) %s\n", vi[i].class, class_names[vi[i].class]);
            printf("red_mask: 0x%lx\n", vi[i].red_mask);
            printf("green_mask: 0x%lx\n", vi[i].green_mask);
            printf("blue_mask: 0x%lx\n", vi[i].blue_mask);
            printf("colormap_size: %d\n", vi[i].colormap_size);
            printf("bits_per_rgb: %d\n", vi[i].bits_per_rgb);
        }

        XFree(vi);
    }

    XCloseDisplay(d);

    return 0;
}

デフォルトのスクリーンでサポートされている、ビジュアルのリストを表示します。
※数百個表示される場合があるため、「$ ./exe > out.txt」のように、結果をテキストに出力してから確認してみてください。

うちの環境だと、24bit,32bit の depth で 600 個のリストが表示されました。

これは、OpenGL などと関連付けられたビジュアルが存在するため、内部では、XVisualInfo 構造体では取得できない、より詳細な値が存在するためです。
XGetVisualInfo 関数
XVisualInfo *XGetVisualInfo(Display *display, long vinfo_mask,
    XVisualInfo *vinfo_template, int *nitems_return);

vinfo_template で指定された値と等しい値を持つ XVisualInfo 構造体のリストを返します。
返されたポインタは、XFree() で解放します。

vinfo_mask は、vinfo_template で使用するメンバのマスクです。
VisualNoMask, VisualAllMask
VisualIDMask, VisualScreenMask, VisualDepthMask, VisualClassMask, VisualRedMaskMask
VisualGreenMaskMask, VisualBlueMaskMask, VisualColormapSizeMask, VisualBitsPerRGBMask

今回の場合、テンプレートの XVisualInfo 構造体の screen メンバに、デフォルトのスクリーン番号を入れて、vinfo_mask に VisualScreenMask を指定することで、指定スクリーン番号に一致するビジュアルを返します。
XVisualInfo 構造体
typedef struct {
 Visual *visual;
 VisualID visualid;
 int screen;
 unsigned int depth;
 int class;
 unsigned long red_mask;
 unsigned long green_mask;
 unsigned long blue_mask;
 int colormap_size;
 int bits_per_rgb;
} XVisualInfo;

viaualVisual 構造体のポインタ
visualidビジュアルのリソース ID (XID)
screenスクリーン番号
depthビット数
classクラス。

StaticGray (0) : (グレイスケール) ピクセル値はインデックス。カラーマップは固定。
GrayScale (1) : StaticGray と同じだが、カラーマップは変更可能。
StaticColor (2) : (パレットカラー) ピクセル値はインデックス。カラーマップは固定。
PseudoColor (3) : StaticColor と同じだが、カラーマップは変更可能。
TrueColor (4) : ピクセル値は RGB 値をパックしたもの。カラーマップは固定。
DirectColor (5) : TrueColor と同じだが、カラーマップは変更可能。
red_mask
green_mask
blue_mask
(TrueColor, DirectColor 時) ピクセル値から R,G,B 値を取得するためのマスク値
colormap_size新しく作成されたカラーマップで使用可能な、カラーマップエントリの数。
DirectColor と TrueColor の場合、R,G,B のサイズ (それぞれ 8bit なら 256)。
bits_per_rgbR,G,B の各値のビット数

X は古いシステムのため、256 色カラーのモニタにも対応しています。
しかし、現在のモニタは基本的に 24bit か 32bit のフルカラーを使用するので、現状では TrueColor か DirectColor を使用することになります (通常は TrueColor)。
ピクセル値
「ピクセル値」は、X で描画色や背景色を指定する時の色値です。
XImage などのイメージバッファで、各ピクセルの数値を扱うときにも使います。

TrueColor と DirectColor の場合は、1つの整数 (16bit or 32bit) に、R,G,B の3つの値をパックします。
「red_mask = 0xff0000, green_mask = 0xff00, blue_mask = 0xff」の場合は、0xRRGGBB という形の整数値になります。

depth が 24bit,32bit の場合は、0xRRGGBB の形になることが多いですが、それ以外 (0xBBGGRR など) になる場合もあります。
GLX でビジュアルの詳細な情報を取得
上記のプログラムで出力された情報を見てみると、visualid を除いて、同じ構成のビジュアルが複数個存在しているのがわかります。

これは、拡張機能 GLX (X で OpenGL を使用するためのもの) によって、ビジュアル内に隠れた値が存在しているためです。
それぞれのビジュアルの詳細な情報を取得したい場合は、GLX を使う必要があります。
プログラム (2)
デフォルトのスクリーンで使用可能な、すべての GLXFBConfig (設定の情報) を取得し、そこに X のビジュアル ID が関連付けられている場合は、情報を表示します。

-lGLX で、GLX のライブラリをリンクする必要があります。
Ubuntu/debuan の場合、libglx-dev パッケージが必要です。

$ cc -o run 03b-glx.c -lX11 -lGLX

<03b-glx.c>
#include <stdio.h>
#include <X11/Xlib.h>
#include <GL/glx.h>

int main(int argc,char **argv)
{
    Display *disp;
    Visual *visual;
    GLXFBConfig *pcf,cf;
    int major,minor,i,num,cnt,val,r,g,b,a;
    
    disp = XOpenDisplay(NULL);
    if(!disp) return 1;

    visual = DefaultVisual(disp, DefaultScreen(disp));
    
    printf("default visual id: %lu\n", XVisualIDFromVisual(visual));

    //GLX のバージョン取得
    
    if(!glXQueryVersion(disp, &major, &minor)
        || (major == 1 && minor < 4))
    {
        printf("unsupported GLX ver 1.4\n");
        XCloseDisplay(disp);
        return 1;
    }

    //指定スクリーンで使用可能なすべての GLXFBConfig

    pcf = glXGetFBConfigs(disp, DefaultScreen(disp), &num);
    if(pcf)
    {
        cnt = 0;
        
        for(i = 0; i < num; i++)
        {
            cf = pcf[i];

            glXGetFBConfigAttrib(disp, cf, GLX_VISUAL_ID, &val);

            if(val == 0) continue;

            printf("--- [%d] ---\n", cnt++);
            printf("visual_id: %d\n", val);

            glXGetFBConfigAttrib(disp, cf, GLX_BUFFER_SIZE, &val);
            glXGetFBConfigAttrib(disp, cf, GLX_RED_SIZE, &r);
            glXGetFBConfigAttrib(disp, cf, GLX_GREEN_SIZE, &g);
            glXGetFBConfigAttrib(disp, cf, GLX_BLUE_SIZE, &b);
            glXGetFBConfigAttrib(disp, cf, GLX_ALPHA_SIZE, &a);

            printf("buffer: %dbit (R:%d, G:%d, B:%d, A:%d)\n", val, r, g, b, a);

            glXGetFBConfigAttrib(disp, cf, GLX_DEPTH_SIZE, &val);
            printf("zbuf: %dbit\n", val);

            glXGetFBConfigAttrib(disp, cf, GLX_STENCIL_SIZE, &val);
            printf("stencil: %dbit\n", val);
        }

        XFree(pcf);
    }

    XCloseDisplay(disp);

    return 0;
}
情報の比較
03a-visual.c と 03b-glx.c で、出力された情報を比較してみてください。
おそらく、両方で同じビジュアル ID が存在すると思います (順番は多少異なりますが)。

default visual id: 33

[XVisualInfo]

visualid: 33
depth: 24
class: (4) TrueColor
red_mask: 0xff0000
green_mask: 0xff00
blue_mask: 0xff
colormap_size: 256
bits_per_rgb: 8

[GLX]

visual_id: 33
buffer: 32bit (R:8, G:8, B:8, A:8)
zbuf: 24bit
stencil: 8bit

たとえば、この情報を見てみると、ビジュアル ID は 33 で同じですが、depth と buffer のサイズが一致していません。

GLX における buffer は、アルファ値も含めたカラーバッファのビット数です。この場合は A=8bit であることから、32 bit になっています。
しかし、Xlib におけるレンダリングでは、アルファ値は使用できないため、depth は 24 bit になっています。

zbuf と stencil は、3D で使用されるバッファのビット数です。

他にもいくつか属性がありますが、ここでは表示していません。