言いたいことはこのツイートで済んでますがAdCの時期でせっかくなので記事化しました。
システムが持っているWaylandのライブラリを利用してGUIを出現させてみます。
- 執筆時の D version は 2.098.0
- OSはubuntu 20.04
予めシステムヘッダーのwayland-client.hを同.iに変換します(正直ここが本番)。
C11に忠実なライブラリであればあるほど変換は楽でしょう。
なお後述のSDLの項で影響受けてますが、コンパイラの実装にひっかかって詰む場合もあるので
当該ライブラリ自体が忠実だとしても、だめなときはだめです。
$ clang -E -std=c11 \
-D__extension__= \
-D__restrict= \
/usr/include/wayland-client.h \
| sed -e 's/(void (\*\*)(void))/(void**)/g' \
-e 's/void (\*\*implementation)(void)/void**/' \
> wayland_client.i
(gccの場合はプリプロセッサに'-D_Float128=long double'
を足すと動いた)
クライアントのコードを書きます。
Wayland側の参考コード
https://qiita.com/propella/items/d180dd0425ecd99efd42
共有バッファの所を大着するためのmemfd_createがD側に用意されてないので直接定義するとか、
一部castを入れてあげないと型競合してエラーになるとか起きるのでそこは注意。
// dmd -L-lwayland-client wl1.d wayland_client.i && ./wl1
import std;
// clang -E -std=c11 -D__extension__= -D__restrict= /usr/include/wayland-client.h |
// sed -e 's/(void (\*\*)(void))/(void**)/g' -e 's/void (\*\*implementation)(void)/void**/' > wayland_client.i
import wayland_client;
import core.sys.linux.unistd;
import core.sys.linux.sys.mman;
auto myassert(T)(T eval){ writeln(eval); assert(eval); return eval; }
__gshared wl_compositor* g_compositor;
__gshared wl_shell* g_shell;
__gshared wl_shm* g_shm;
extern(C){
void reg_global(void* data, wl_registry* reg, uint id, const(char)* iface, uint ver){
auto iface_ = iface.fromStringz;
switch(iface_){
case "wl_compositor":
g_compositor = cast(wl_compositor*)wl_registry_bind(reg, id, &wl_compositor_interface, 1).myassert;
break;
case "wl_shell":
g_shell = cast(wl_shell*)wl_registry_bind(reg, id, &wl_shell_interface, 1).myassert;
break;
case "wl_shm":
g_shm = cast(wl_shm*)wl_registry_bind(reg, id, &wl_shm_interface, 1).myassert;
break;
default:
printf("%p %p %d %s %d\n", data, reg, id, iface, ver);
}
}
void shell_ping(void* data, wl_shell_surface* shsrf, uint serial){
printf("%p %p %d\n", data, shsrf, serial);
wl_shell_surface_pong(shsrf, serial);
}
int memfd_create(const(char)*, uint);
}
void main(){
auto dpy = wl_display_connect(null).myassert;
auto reg = wl_display_get_registry(dpy).myassert;
wl_registry_add_listener(reg, cast(wl_registry_listener*)[cast(void*)®_global, null], null);
wl_display_roundtrip(dpy);
auto srf = wl_compositor_create_surface(g_compositor).myassert;
auto shsrf = wl_shell_get_shell_surface(g_shell, srf);
wl_shell_surface_set_toplevel(shsrf);
wl_shell_surface_add_listener(shsrf, cast(wl_shell_surface_listener*)[&shell_ping, null, null], null);
auto shmfd = memfd_create("".ptr, 0).myassert;
auto shmsize = 128*128*4;
ftruncate(shmfd, shmsize);
void* shmptr = mmap(null, shmsize, PROT_READ|PROT_WRITE, MAP_SHARED, shmfd, 0).myassert;
shmptr[0..128*128*4] = iota(128*128*4).map!(a=>uniform!(ubyte)).array;
auto shmpool = wl_shm_create_pool(g_shm, shmfd, shmsize);
auto shmbuf = wl_shm_pool_create_buffer(shmpool, 0, 128, 128, 128*4, WL_SHM_FORMAT_ARGB8888);
wl_shm_pool_destroy(shmpool);
wl_surface_attach(srf, shmbuf, 0, 0);
wl_surface_commit(srf);
while(wl_display_dispatch(dpy) != -1){}
}
あとはライブラリを指定してビルド&実行。
$ dmd -L-lwayland-client wl1.d wayland_client.i && ./wl1
本編ここまで、以下失敗例
SDLでも途中まで試しましたが、定数(SDL_INIT_VIDEOとか)がdefineされてるので蒸発するとかで一筋縄では無理そうでした。
$ cat sdl1.d
import std;
import SDL;
void main(){
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
// 窓を出す実装を書くまでもなく詰んだ
}
$ clang -E -std=c11 -D__restrict= -D__inline__=inline -D__inline=inline \
-U__GNUC__ '-D__int128=long long' -fno-gnu-inline-asm \
/usr/include/SDL2/SDL.h -I/usr/include/SDL2 > SDL.i
$ dmd -L-lSDL2 sdl1.d SDL.i
/usr/include/SDL2/SDL_audio.h(192): Error: struct `SDL.SDL_AudioCVT` no size because of forward reference
Segmentation fault (コアダンプ)
$ vim SDL.i
(↓xを消す、aとbを入れ替え)
xstruct SDL_AudioCVT;
atypedef void ( * SDL_AudioFilter) (struct SDL_AudioCVT * cvt,
a SDL_AudioFormat format);
b# 226 "/usr/include/SDL2/SDL_audio.h"
btypedef struct SDL_AudioCVT
b{
b int needed;
b SDL_AudioFormat src_format;
b SDL_AudioFormat dst_format;
b double rate_incr;
b Uint8 *buf;
b int len;
b int len_cvt;
b int len_mult;
b double len_ratio;
b SDL_AudioFilter filters[9 + 1];
b int filter_index;
b} SDL_AudioCVT;
$ dmd -L-lSDL2 sdl1.d SDL.i
sdl1.d(5): Error: undefined identifier `SDL_INIT_VIDEO`
sdl1.d(5): Error: undefined identifier `SDL_INIT_EVENTS`
/usr/include/SDL2/SDL_stdinc.h(437): Error: `switch` statement without a `default`; use `final switch` or add `default: assert(0);` or add `default: break;`
/usr/include/SDL2/SDL_stdinc.h(437): Error: `object` is not a member of `void`
/usr/include/SDL2/SDL_stdinc.h(441): Deprecation: switch case fallthrough - use 'goto case;' if intended
/usr/include/SDL2/SDL_stdinc.h(442): Deprecation: switch case fallthrough - use 'goto case;' if intended
/usr/include/SDL2/SDL_stdinc.h(441): Deprecation: switch case fallthrough - use 'goto case;' if intended
/usr/include/SDL2/SDL_stdinc.h(442): Deprecation: switch case fallthrough - use 'goto case;' if intended
/usr/lib/llvm-10/lib/clang/10.0.0/include/mmintrin.h(33): Error: undefined identifier `__builtin_ia32_emms`
/usr/lib/llvm-10/lib/clang/10.0.0/include/mmintrin.h(50): Error: undefined identifier `__builtin_ia32_vec_init_v2si`
/usr/lib/llvm-10/lib/clang/10.0.0/include/mmintrin.h(67): Error: undefined identifier `__builtin_ia32_vec_ext_v2si`
/usr/lib/llvm-10/lib/clang/10.0.0/include/mmintrin.h(129): Error: undefined identifier `__builtin_ia32_packsswb`
/usr/lib/llvm-10/lib/clang/10.0.0/include/mmintrin.h(159): Error: undefined identifier `__builtin_ia32_packssdw`
/usr/lib/llvm-10/lib/clang/10.0.0/include/mmintrin.h(189): Error: undefined identifier `__builtin_ia32_packuswb`
/usr/lib/llvm-10/lib/clang/10.0.0/include/mmintrin.h(216): Error: undefined identifier `__builtin_ia32_punpckhbw`
/usr/lib/llvm-10/lib/clang/10.0.0/include/mmintrin.h(239): Error: undefined identifier `__builtin_ia32_punpckhwd`
/usr/lib/llvm-10/lib/clang/10.0.0/include/mmintrin.h(260): Error: undefined identifier `__builtin_ia32_punpckhdq`
/usr/lib/llvm-10/lib/clang/10.0.0/include/mmintrin.h(287): Error: undefined identifier `__builtin_ia32_punpcklbw`
/usr/lib/llvm-10/lib/clang/10.0.0/include/mmintrin.h(310): Error: undefined identifier `__builtin_ia32_punpcklwd`
/usr/lib/llvm-10/lib/clang/10.0.0/include/mmintrin.h(331): Error: undefined identifier `__builtin_ia32_punpckldq`
/usr/lib/llvm-10/lib/clang/10.0.0/include/mmintrin.h(352): Error: undefined identifier `__builtin_ia32_paddb`
/usr/lib/llvm-10/lib/clang/10.0.0/include/mmintrin.h(373): Error: undefined identifier `__builtin_ia32_paddw`
/usr/lib/llvm-10/lib/clang/10.0.0/include/mmintrin.h(394): Error: undefined identifier `__builtin_ia32_paddd`
/usr/lib/llvm-10/lib/clang/10.0.0/include/mmintrin.h(416): Error: undefined identifier `__builtin_ia32_paddsb`
fallthroughのところはSDLのmasterだと実装が少し変わってるのでそれをつかえば動くかも。
あとintrinsicを無効にする方法とかがわからぬ(無理やり無効化したところで動くかもわからぬ)。