X11 でスクリーンキャプチャー
で X11 でも.
- ルートウィンドウを取得
- XGetImage() でルートウィンドウのコピーとなる XImage を得る
- XImage はクライアント側のラスター画像でピクセルにアクセスできるので好きにいじる
でも XGetImage() で取得できる XImage ってルートウィンドウと同じ depth やカラーマップを持つものなるのでXサーバの設定によってフォーマットが変ることになる. X って depth がいろいろあって(32, 24, 16, 8 はまだしも 4 とか 1 とかもありえる)個別に処理するのは面倒なんですよねぇ… できればこちらで指定したフォーマット(32 bpp RGBとか)に X11 側で出力してくれるか,あるいは取得した XImage のフォーマットの変換が API で簡単にできるといいんだけど Xlib ではできなかったんだっけ?
フォーマット変換の方法として以下の手順をを考えてみた.
- XCreatePixmap() でサーバ側に 32 bpp の Pixmap を作成
- XCopyArea() でルートウィンドウの内容を Pixmap にコピー
- XGetImage() で Pixmap の内容を XImage として取得
しかし XCopyArea() は同一の depth の Window や Pixmap の間でしかコピーできない模様で挫折.
こういうフォーマットの変換って Xlib の API では無理で自前でやるしかないんだっけか? 何か X 拡張でそういう機能があった気もしたけど思い出せない… まぁ X 拡張じゃあまり嬉しくないですが…
以下 32 bpp RGB な場合限定のサンプル.
/** * gcc capture_screen_x11-01.c -L/usr/X11/lib -lX11 **/ #include <stdio.h> #include <stdint.h> #include <assert.h> #include <X11/Xlib.h> #define IMAGE_FILE_PATH "capture_screen.ppm" static void writeXImageToP3File(XImage *image, const char *file_path) { FILE *file; file = fopen(file_path, "wb"); if (file != NULL) { int x, y; char *cp; uint32_t pixel; fprintf(file, "P3\n"); fprintf(file, "%d %d\n", image->width, image->height); fprintf(file, "255\n"); for (y = 0;y < image->height;y++) { cp = image->data + y * image->bytes_per_line; for (x = 0;x < image->width;x++) { pixel = *(uint32_t *)cp; fprintf(file, "%d %d %d ", (pixel & 0x00ff0000) >> 16, (pixel & 0x0000ff00) >> 8, (pixel & 0x000000ff) >> 0); cp += 4; } fprintf(file, "\n"); } fclose(file); } } int main(int argc, char* argv[]) { Display* display; int screen; Window rootWindow; XWindowAttributes attributes; int width, height; XImage *image; display = XOpenDisplay(""); screen = DefaultScreen(display); rootWindow = RootWindow(display, screen); XGetWindowAttributes(display, rootWindow, &attributes); width = attributes.width; height = attributes.height; image = XGetImage(display, rootWindow, 0, 0, attributes.width, attributes.height, AllPlanes, ZPixmap); if (image != NULL) { if (image->bits_per_pixel == 32) writeXImageToP3File(image, IMAGE_FILE_PATH); else fprintf(stderr, "Not Supported format : bits_per_pixel = %d\n", image->bits_per_pixel); XFree(image); } XCloseDisplay(display); return 0; }