主頁 | 自己紹介 | 日記 | 農業 | 台所 | 電算機 | | 本棚 | | 植物 | Git

X11で深さが32bitのwindowを作成する

結論

X11のrender拡張機能で半透明な画像を合成(composite)する際に深さが32bitのウィンドウを作成する必要がある。その際ドキュメントにも書いてなさそうな操作が必要だった。ウィンドウを作成する際にborder pixelの値を設定する必要がある(言語はC):

// Xlibの場合
XSetWindowAttributes attr32 = {
	.border_pixel = 0, // 値はなんでもよさそう
	.colormap = colormap32,
}
Window window32 = XCreateWindow(display, DefaultRootWindow(display),
	0, 0, 1, 1, 0,
	32, // depth
	InputOutput,
	visual32,
	CWBorderPixel|CWColormap, // CWBorderPixelを指定
	&attr32);
// xcbの場合
uint32_t attr32[2] = { 0, colormap32 }; // 値はなんでもよさそう。
xcb_create_window(conn, 32, window32, screen->root,
	0, 0, 1, 1, 0,
	XCB_WINDOW_CLASS_INPUT_OUTPUT,
	visual32,
	XCB_CW_BORDER_PIXEL|XCB_CW_COLORMAP, // XCB_CW_BORDER_PIXELを指定
	&attr32);

出典

Go言語のgolang.org/x/exp/shiny/driver/x11driver/screen.go:622に、

	// The CwBorderPixel attribute seems necessary for depth == 32. See
	// http://stackoverflow.com/questions/3645632/how-to-create-a-window-with-a-bit-depth-of-32

と書いていた。引用元を見るとXサーバーのソースコードを参照しており、そのコードでは親のウィンドウの深さと異なる深さのウィンドウを作成する場合、CWBorderPixmap又はCWBorderPixelを指定しないとBadMatchというエラーが返ってくるようになっている。理由はよく分からない:

	if (((vmask & (CWBorderPixmap | CWBorderPixel)) == 0) &&
		(class != InputOnly) && (depth != pParent->drawable.depth)) {
		*error = BadMatch;
		return NullWindow;
	}

深さ32bitのウィンドウの作成手順

深さ32bitのヴィジュアルを取得

ウィンドウ作成の関数(XCreateWindowまたはxcb_create_window)に渡す32bitのヴィジュアルを取得する:

// xlibの場合
int findVisual32(Display *display, XVisualInfo *return_visual_info);
/* ... */
int main(void) {
	/* ... */
	XVisualInfo vi32;
	if (findVisual32(display, &vi32) < 0) {
		fatal("32bit-deep visual info not found");
	}
	/* ... */
}
/* ... */
int findVisual32(Display *display, XVisualInfo *return_visual_info) {
	long mask = VisualDepthMask|VisualRedMaskMask|VisualGreenMaskMask|VisualBlueMaskMask;
	XVisualInfo template = {
		.depth      = 32,
		.red_mask   = 0xff0000,
		.green_mask = 0x00ff00,
		.blue_mask  = 0x0000ff,
	};
	int n;
	XVisualInfo *vinfos = XGetVisualInfo(display, mask, &template, &n);
	if (n == 0) {
		return -1;
	}
	*return_visual_info = vinfos[0];
	XFree(vinfos);
	return 0;
}
// xcbの場合
int findVisual32(xcb_screen_t *screen, xcb_visualtype_t *return_visualtype);
/* ... */
int main(void) {
	/* ... */
	xcb_visualtype_t visual32;
	if (findVisual32(screen, &visual32) < 0) {
		fatal("32bit visual not found");
	}
	/* ... */
}
/* ... */
int findVisual32(xcb_screen_t *screen, xcb_visualtype_t *return_visualtype) {
	xcb_depth_iterator_t depth_iter = xcb_screen_allowed_depths_iterator(screen);
	for (;depth_iter.rem > 0; xcb_depth_next(&depth_iter)) {
		xcb_depth_t *d = depth_iter.data;
		if (d->depth != 32) {
			continue;
		}
		xcb_visualtype_iterator_t visual_iter = xcb_depth_visuals_iterator(d);
		while (visual_iter.rem > 0) {
			xcb_visualtype_t *v = visual_iter.data; 
			if (v->red_mask == 0xff0000 && v->green_mask == 0x00ff00 && v->blue_mask == 0x0000ff) {
				*return_visualtype = *v;
				return 0;
			}
			xcb_visualtype_next(&visual_iter);
		}
	}
	return -1;
}

深さ32bitのカラーマップを作成

ウィンドウ作成の関数(XCreateWindowまたはxcb_create_window)に渡す32bitのカラーマップを作成する。

// xlibの場合
	Colormap colormap32 = XCreateColormap(display, DefaultRootWindow(display), vi32.visual, AllocNone);
// xcbの場合
	xcb_colormap_t colormap32 = xcb_generate_id(conn);
	xcb_create_colormap(conn, XCB_COLORMAP_ALLOC_NONE, colormap32, screen->root, visual32.visual_id);

深さ32bitのウィンドウを作成

最後にウィンドウを作成する:

// xlibの場合
	Window window32 = XCreateWindow(display, DefaultRootWindow(display),
		0, 0, 1, 1, 0,
		32,
		InputOutput,
		vi32.visual,
		CWBorderPixel|CWColormap,
		&attr32);
// xcbの場合
	uint32_t attr32[2] = { 0, colormap32 }; 
	xcb_create_window(conn, 32, window32, screen->root,
		0, 0, 1, 1, 0,
		XCB_WINDOW_CLASS_INPUT_OUTPUT,
		visual32.visual_id,
		XCB_CW_BORDER_PIXEL|XCB_CW_COLORMAP, &attr32);

サンプルコード

32bitのウィンドウを作成し、そこからrender拡張機能を使って半透明な四角形を描画するプログラムを作った。OpenBSDで動作確認した。他のUnixでも多分動くと思う:
git.mtkn.jp/win32

参考文献

  1. X11: ビジュアル