ウィンドウ用イメージを作る
ここでは、実際に、共有メモリでウィンドウ表示用のイメージを作成してみます。
今回はイメージを作成するだけなので、まだウィンドウは表示できません。
今回はイメージを作成するだけなので、まだウィンドウは表示できません。
プログラム
$ cc -o test 04_shmimg.c -lwayland-client -lrt
shm_open() を使うのに librt が必要なので、リンクを追加します。
<04_shmimg.c>
/****************************** * 共有メモリイメージを作成 ******************************/ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #include <unistd.h> #include <wayland-client.h> //-------------- struct wl_shm *g_shm; unsigned int g_shm_cnt = 0; //共有メモリ名用 typedef struct { struct wl_shm_pool *pool; struct wl_buffer *buffer; void *data; int size; }imagebuf; //------------- /* POSIX 共有メモリオブジェクトを作成 * * return: shm_open() の戻り値 (成功時はファイルディスクリプタ) */ static int _create_posix_shm() { char name[64]; int ret; while(1) { snprintf(name, 64, "/wayland-test-%d", g_shm_cnt); ret = shm_open(name, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, 0600); if(ret >= 0) { //成功 shm_unlink(name); g_shm_cnt++; break; } else if(errno == EEXIST) //同名が存在する -> カウントを+1して次へ g_shm_cnt++; else if(errno != EINTR) { printf("failed shm_open()\n"); break; } } return ret; } /* wl_shm_pool 作成 * * size: バッファサイズ * ppbuf: バッファのポインタが入る */ static struct wl_shm_pool *_create_shm_pool(int size,void **ppbuf) { int fd; void *data; struct wl_shm_pool *pool; fd = _create_posix_shm(); if(fd < 0) return NULL; printf("posix shm: %d\n", fd); //サイズ変更 if(ftruncate(fd, size) < 0) { close(fd); return NULL; } //メモリにマッピング data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if(data == MAP_FAILED) { close(fd); return NULL; } printf("mmap: %p\n", data); //wl_shm_pool 作成 pool = wl_shm_create_pool(g_shm, fd, size); //クローズしてもアンマップはされない close(fd); *ppbuf = data; return pool; } /* イメージバッファ作成 */ static imagebuf *imagebuf_create(int width,int height) { imagebuf *img; struct wl_shm_pool *pool; struct wl_buffer *buffer; void *buf; int size; size = width * 4 * height; //wl_shm_pool 作成 pool = _create_shm_pool(size, &buf); if(!pool) return NULL; printf("create shm pool: ok\n"); //wl_buffer 作成 buffer = wl_shm_pool_create_buffer(pool, 0, //offset width, height, width * 4, //Y1行のバイト数 WL_SHM_FORMAT_XRGB8888); if(buffer) printf("create buffer: ok\n"); //imagebuf img = (imagebuf *)malloc(sizeof(imagebuf)); if(!img) return NULL; img->pool = pool; img->buffer = buffer; img->data = buf; img->size = size; return img; } /* imagebuf 削除 */ static void imagebuf_destroy(imagebuf *p) { if(p) { wl_buffer_destroy(p->buffer); wl_shm_pool_destroy(p->pool); munmap(p->data, p->size); free(p); } } //--------------------- // wl_registry //--------------------- static void _registry_global( void *data,struct wl_registry *registry, uint32_t id,const char *interface,uint32_t version) { //wl_shm をバインド if(strcmp(interface, "wl_shm") == 0) g_shm = wl_registry_bind(registry, id, &wl_shm_interface, 1); } static void _registry_global_remove(void *data,struct wl_registry *registry,uint32_t id) { } static const struct wl_registry_listener g_reg_listener = { _registry_global, _registry_global_remove }; //------------ int main(void) { struct wl_display *display; struct wl_registry *reg; imagebuf *img; display = wl_display_connect(NULL); if(!display) return 1; //wl_registry reg = wl_display_get_registry(display); wl_registry_add_listener(reg, &g_reg_listener, NULL); wl_display_roundtrip(display); //イメージ作成 img = imagebuf_create(100, 100); imagebuf_destroy(img); // wl_shm_destroy(g_shm); wl_registry_destroy(reg); wl_display_disconnect(display); return 0; }
共有メモリイメージを作成して、すぐに削除するだけのプログラムです。
posix shm: 4 mmap: 0x7fa8981bd000 create shm pool: ok create buffer: ok
解説
後々使いやすいように、イメージデータとして imagebuf 構造体を作って、共有メモリイメージを作成&削除できるようにしました。
イメージバッファ用のファイルディスクリプタを得るには、shm_open() 関数で POSIX 共有メモリオブジェクトを作成する他に、任意のディレクトリにファイルを作る方法もあります。
今回は shm_open() を使います。
- wl_shm をバインド。
- shm_open() で POSIX 共有メモリオブジェクトを作成。
- 上記関数で得たファイルディスクリプタを使い、メモリにマッピングしてバッファを取得。
- 同じく、fd を使って、wl_shm から wl_shm_pool を作成。
- wl_shm_pool から wl_buffer を作成。
イメージバッファ用のファイルディスクリプタを得るには、shm_open() 関数で POSIX 共有メモリオブジェクトを作成する他に、任意のディレクトリにファイルを作る方法もあります。
今回は shm_open() を使います。
POSIX 共有メモリオブジェクトの作成
共有メモリ用のファイルディスクリプタを取得するために、shm_open() を使います。
name は、最大 NAME_MAX (255) 文字の文字列で、先頭はスラッシュ (/) から始まり、以降は任意の文字列にします。
すでに存在する名前と重複しないように、適当な名前を付けてください。
同じ名前がすでに存在している場合は、errno に EEXIST が入ります。
今回は一つのイメージしか作成しないので、名前は固定名でも問題ありませんが、通常は複数のイメージを作ることにより、その分だけファイルディスクリプタが必要になると思うので、作成するごとに数値のカウントを増やして、名前に付加する形にしています。
shm_open() の戻り値は、-1 で失敗。
0 以上の場合は成功で、ファイルディスクリプタの値となります。
shm_unlink() 関数で、指定名のオブジェクトを削除します。
ファイルの unlink() と同じで、shm_unlink() が実行された後も、オブジェクトが使用されている間は実体が存在していますが、アンマップされた時点で自動的に破棄されます。
なので、今回は作成直後に shm_unlink() して、解放処理を一つ省略しています。
#include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> int shm_open(const char *name, int oflag, mode_t mode); int shm_unlink(const char *name); [!] librt のリンクが必要。
name は、最大 NAME_MAX (255) 文字の文字列で、先頭はスラッシュ (/) から始まり、以降は任意の文字列にします。
すでに存在する名前と重複しないように、適当な名前を付けてください。
同じ名前がすでに存在している場合は、errno に EEXIST が入ります。
今回は一つのイメージしか作成しないので、名前は固定名でも問題ありませんが、通常は複数のイメージを作ることにより、その分だけファイルディスクリプタが必要になると思うので、作成するごとに数値のカウントを増やして、名前に付加する形にしています。
shm_open() の戻り値は、-1 で失敗。
0 以上の場合は成功で、ファイルディスクリプタの値となります。
shm_unlink() 関数で、指定名のオブジェクトを削除します。
ファイルの unlink() と同じで、shm_unlink() が実行された後も、オブジェクトが使用されている間は実体が存在していますが、アンマップされた時点で自動的に破棄されます。
なので、今回は作成直後に shm_unlink() して、解放処理を一つ省略しています。
wl_shm_pool の作成
wl_shm_pool は、共有メモリバッファ用の「プール (溜める場所)」です。
wl_shm_pool は、イメージ一つにつき一個作成するという使い方もできますが、複数の小さいイメージ用に、大きなサイズの wl_shm_pool を一個作っておいて、そこから一部分の範囲のバッファを各イメージが使う、といった使い方も出来ます。
まずは、ftruncate(fd, size) で、必要なバッファのサイズに変更します。
今回は WL_SHM_FORMAT_XRGB8888 フォーマットを使うので、「1 px = 4 byte」により、「幅×高さ×4 byte」 のサイズになります。
mmap() の戻り値は、成功すればバッファのポインタです。
失敗時は、MAP_FAILED が返ります。
その後、close(fd) でファイルディスクリプタをクローズしています。
mmap() されたバッファは、munmap() を行うまでは、fd をクローズしてもアンマップされないので、解放処理の省略のため、ここでクローズしています。
wl_shm_pool は、イメージ一つにつき一個作成するという使い方もできますが、複数の小さいイメージ用に、大きなサイズの wl_shm_pool を一個作っておいて、そこから一部分の範囲のバッファを各イメージが使う、といった使い方も出来ます。
サイズ変更
shm_open() で作成した共有メモリオブジェクトは、O_CREAT を指定しているため、初期サイズが 0 となっています。まずは、ftruncate(fd, size) で、必要なバッファのサイズに変更します。
今回は WL_SHM_FORMAT_XRGB8888 フォーマットを使うので、「1 px = 4 byte」により、「幅×高さ×4 byte」 のサイズになります。
メモリにマッピング
次に、mmap() で、メモリにマッピングします。#include <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); int munmap(void *addr, size_t length);
mmap() の戻り値は、成功すればバッファのポインタです。
失敗時は、MAP_FAILED が返ります。
wl_shm_pool 作成
メモリにマッピングできたら、wl_shm_create_pool() で wl_shm_pool を作成します。struct wl_shm_pool *wl_shm_create_pool(struct wl_shm *wl_shm, int32_t fd, int32_t size);
その後、close(fd) でファイルディスクリプタをクローズしています。
mmap() されたバッファは、munmap() を行うまでは、fd をクローズしてもアンマップされないので、解放処理の省略のため、ここでクローズしています。
wl_buffer の作成
wl_shm_pool が作成できたら、wl_shm_pool_create_buffer() で wl_buffer を作成し、これでようやく Wayland のイメージバッファが作れました。
offset は、プール内のバッファのオフセット位置です。
一つのプールで複数の wl_buffer を作る場合は、その先頭位置を指定します。
stride は、Y 1 行のバイト数です。
struct wl_buffer *wl_shm_pool_create_buffer( struct wl_shm_pool *wl_shm_pool, int32_t offset, int32_t width, int32_t height, int32_t stride, uint32_t format);
offset は、プール内のバッファのオフセット位置です。
一つのプールで複数の wl_buffer を作る場合は、その先頭位置を指定します。
stride は、Y 1 行のバイト数です。
解放処理
必要な解放処理は、以下の通りです。wl_buffer_destroy(p->buffer); wl_shm_pool_destroy(p->pool); munmap(p->data, p->size);