#include #include #include #include #include #include // xdg-shell replaces the deprecated wl_shell interface. Modern compositors // (Mutter, KWin, wlroots-based, etc.) no longer advertise wl_shell, so binding // it returned NULL and the first wl_shell_* call crashed inside libwayland. // This header is generated from xdg-shell.xml by wayland-scanner (see compile.sh). #include "xdg-shell-client-protocol.h" struct wl_compositor *compositor; struct wl_shm *shm; // Replaced `struct wl_shell *shell;` with the xdg-shell window-management global. struct xdg_wm_base *wm_base; // The compositor periodically pings the client to check it is still responsive. // We must answer with a pong or the compositor may consider us hung. wl_shell // had an equivalent ping on wl_shell_surface; with xdg-shell the ping lives on // xdg_wm_base, so we handle it here. void wm_base_ping_handler ( void *data, struct xdg_wm_base *wm_base, uint32_t serial ) { xdg_wm_base_pong(wm_base, serial); } const struct xdg_wm_base_listener wm_base_listener = { .ping = wm_base_ping_handler }; void registry_global_handler ( void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version ) { if (strcmp(interface, "wl_compositor") == 0) { compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 3); } else if (strcmp(interface, "wl_shm") == 0) { shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); } else if (strcmp(interface, "xdg_wm_base") == 0) { // Replaced the "wl_shell" branch: bind xdg_wm_base instead and start // listening for its ping event straight away. wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); xdg_wm_base_add_listener(wm_base, &wm_base_listener, NULL); } } void registry_global_remove_handler ( void *data, struct wl_registry *registry, uint32_t name ) {} const struct wl_registry_listener registry_listener = { .global = registry_global_handler, .global_remove = registry_global_remove_handler }; // xdg-shell uses an explicit configure/ack handshake for surfaces. When the // compositor sends a configure event, the client must acknowledge it with // xdg_surface_ack_configure. This has no wl_shell equivalent (wl_shell let you // attach a buffer immediately), and skipping it is a protocol error on some // compositors. We commit the surface again after acking so the first frame // becomes visible. void xdg_surface_configure_handler ( void *data, struct xdg_surface *xdg_surface, uint32_t serial ) { xdg_surface_ack_configure(xdg_surface, serial); } const struct xdg_surface_listener xdg_surface_listener = { .configure = xdg_surface_configure_handler }; int main(void) { struct wl_display *display = wl_display_connect(NULL); struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); // wait for the "initial" set of globals to appear wl_display_roundtrip(display); // Fail loudly if a required global is missing instead of dereferencing NULL // later (which is what caused the original SIGSEGV in wl_proxy_get_version). if (!compositor || !shm || !wm_base) { fprintf(stderr, "missing required global: compositor=%p shm=%p wm_base=%p\n", (void *)compositor, (void *)shm, (void *)wm_base); return 1; } struct wl_surface *surface = wl_compositor_create_surface(compositor); // Replaced the wl_shell surface setup: // struct wl_shell_surface *shell_surface = wl_shell_get_shell_surface(shell, surface); // wl_shell_surface_set_toplevel(shell_surface); // with the xdg-shell equivalent. In xdg-shell a role is assigned in two // steps: first wrap the wl_surface in an xdg_surface, then give that a // toplevel role via xdg_toplevel (the analog of set_toplevel). struct xdg_surface *xdg_surface = xdg_wm_base_get_xdg_surface(wm_base, surface); xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL); struct xdg_toplevel *xdg_toplevel = xdg_surface_get_toplevel(xdg_surface); xdg_toplevel_set_title(xdg_toplevel, "square"); // An initial commit (with no buffer attached) is required so the compositor // sends the first xdg_surface configure event. We roundtrip to receive and // ack it before attaching a buffer. wl_shell did not need this handshake. wl_surface_commit(surface); wl_display_roundtrip(display); int width = 200; int height = 200; int stride = width * 4; int size = stride * height; // bytes // open an anonymous file and write some zero bytes to it int fd = syscall(SYS_memfd_create, "buffer", 0); ftruncate(fd, size); // map it to the memory unsigned char *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); // turn it into a shared memory pool struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size); // allocate the buffer in that pool struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, WL_SHM_FORMAT_XRGB8888); wl_surface_attach(surface, buffer, 0, 0); wl_surface_commit(surface); while (1) { wl_display_dispatch(display); } }