#include #include #include #include #include #include #include #include #include #include "xdg-shell.prot.h" // Not included in any standard header // Thus, declaring manually. // See memfd_create(2). int memfd_create(const char* name, unsigned int flags); #define ASSERT(cond, msg)\ if(!(cond)) {\ fprintf(stderr, "Runtime assertion %s at %s:%d failed: %s\n", #cond, __FILE__, __LINE__, msg);\ exit(-1);\ } typedef struct state { struct wl_shm* wl_shm; struct wl_compositor* wl_compositor; struct xdg_wm_base* xdg_wm_base; struct wl_surface* surface; struct wl_buffer* buffer; uint32_t conf_x; uint32_t conf_y; uint32_t curr_x; uint32_t curr_y; cairo_surface_t* sfc; cairo_t* ctx; void* shm_data; int shm_fd; uint8_t should_close; } state_t; state_t state; void noop_wl_registry(void* data, struct wl_registry* object, uint32_t serial) {} void noop_xdg_toplevel(void* data, struct xdg_toplevel* object, uint32_t serial) {} void handle_global( void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) { if(strcmp(interface, wl_shm_interface.name) == 0) { state.wl_shm = wl_registry_bind(registry, name, &wl_shm_interface, version); } if(strcmp(interface, wl_compositor_interface.name) == 0) { state.wl_compositor = wl_registry_bind(registry, name, &wl_compositor_interface, version); } if(strcmp(interface, xdg_wm_base_interface.name) == 0) { state.xdg_wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, version); } } void handle_global_remove( void* data, struct wl_registry* registry, uint32_t name) { // Who cares } const struct wl_registry_listener reg_callbacks = { .global = handle_global, .global_remove = noop_wl_registry, }; void resize_buffer(int32_t, int32_t); void draw(); static void xdg_surface_handle_configure( void *data, struct xdg_surface* xdg_surface, uint32_t serial) { xdg_surface_ack_configure(xdg_surface, serial); static uint8_t configured; if (configured) { wl_surface_commit(state.surface); } else { configured = 1; } } void xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *states) { if(width == 0 || height == 0) return; state.conf_x = width; state.conf_y = height; printf("x: %d y: %d\n", width, height); } void xdg_toplevel_handle_configure_bounds(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height) { } void xdg_toplevel_handle_wm_capabilities(void *data, struct xdg_toplevel *xdg_toplevel, struct wl_array *capabilities) { } static void xdg_toplevel_handle_close( void *data, struct xdg_toplevel* xdg_surface) { state.should_close = 1; } const struct xdg_surface_listener xdg_surface_listener = { .configure = xdg_surface_handle_configure, }; const struct xdg_toplevel_listener xdg_toplevel_listener = { .configure = xdg_toplevel_handle_configure, .close = xdg_toplevel_handle_close, .configure_bounds = xdg_toplevel_handle_configure_bounds, .wm_capabilities = xdg_toplevel_handle_wm_capabilities, }; void resize_buffer(int32_t width, int32_t height) { // Buffer params int chan = 4; int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); int size = height * stride; state.curr_x = width; state.curr_y = height; // Create & map memfd close(state.shm_fd); state.shm_fd = memfd_create("wl_shm data", 0); ASSERT(state.shm_fd != -1, "Failed to create memfd."); ASSERT(ftruncate(state.shm_fd, size) != -1, "Failed to ftruncate the memfd."); state.shm_data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, state.shm_fd, 0); ASSERT(state.shm_data != MAP_FAILED, "Failed to mmap the memfd."); // Make pool from memfd struct wl_shm_pool* pool = wl_shm_create_pool(state.wl_shm, state.shm_fd, size); ASSERT(pool != NULL, "Failed to create the wl_shm_pool."); // Allocate full Wayland buffer state.buffer = wl_shm_pool_create_buffer(pool, 0, width, height, chan * width, WL_SHM_FORMAT_ARGB8888); ASSERT(state.buffer != NULL, "Failed to create the wl_buffer."); wl_shm_pool_destroy(pool); // Debugging data memset(state.shm_data, 00, size); cairo_destroy(state.ctx); cairo_surface_destroy(state.sfc); // Initialize cairo state.sfc = cairo_image_surface_create_for_data(state.shm_data, CAIRO_FORMAT_ARGB32, width, height, stride); ASSERT(cairo_surface_status(state.sfc) == CAIRO_STATUS_SUCCESS, "Failed to create the cairo surface."); state.ctx = cairo_create(state.sfc); } void draw() { // Paint background cairo_set_source_rgba(state.ctx, 0.87, 0.68, 0.76, 1.0); cairo_paint(state.ctx); cairo_surface_flush(state.sfc); // Commit buffer wl_surface_damage_buffer(state.surface, 0, 0, INT32_MAX, INT32_MAX); wl_surface_attach(state.surface, state.buffer, 0, 0); wl_surface_commit(state.surface); } int main() { // Connect struct wl_display* dpy = wl_display_connect(NULL); ASSERT(dpy != NULL, "Unable to connect to Wayland"); // Fetch globals struct wl_registry* registry = wl_display_get_registry(dpy); wl_registry_add_listener(registry, ®_callbacks, NULL); wl_display_roundtrip(dpy); ASSERT(state.wl_shm != NULL, "core.wl_shm not supported."); ASSERT(state.wl_compositor != NULL, "core.wl_compositor not supported."); ASSERT(state.xdg_wm_base != NULL, "xdg_shell.xdg_wm_base not supported."); resize_buffer(3440, 1440); // Create Wayland surface state.surface = wl_compositor_create_surface(state.wl_compositor); ASSERT(state.surface != NULL, "Failed to create the wl_surface."); struct xdg_surface* xdg_surface = xdg_wm_base_get_xdg_surface(state.xdg_wm_base, state.surface); ASSERT(xdg_surface != NULL, "Failed to create the xdg_surface."); struct xdg_toplevel* xdg_toplevel = xdg_surface_get_toplevel(xdg_surface); ASSERT(xdg_toplevel != NULL, "Failed to create the xdg_toplevel."); xdg_toplevel_set_title(xdg_toplevel, "Hello, world!"); xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL); xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL); wl_surface_commit(state.surface); wl_display_dispatch(dpy); // Attach Wayland buffer to Wayland surface wl_surface_attach(state.surface, state.buffer, 0, 0); wl_surface_commit(state.surface); wl_display_dispatch(dpy); // Loop until should close while(!state.should_close) { wl_display_dispatch(dpy); //printf("Refresh\n"); if(state.curr_x != state.conf_x || state.curr_y != state.conf_y) { resize_buffer(state.conf_x, state.conf_y); draw(); } } return 0; }