#include #include #include #include #define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include struct wl_compositor *compositor; struct wl_shm *shm; struct xdg_wm_base *xdg_shell; struct wl_seat *wl_seat; struct keyboard_data keyboard_data; struct pointer_data pointer_data; int running = 1; int released = 0; struct timespec mytime; struct canvas { int width; int height; int stride; int size; struct wl_buffer *buffer; unsigned char *data; struct wl_surface *surface; struct wl_callback *frame; } canvas; // registry void registry_global_handler( void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { // printf(" -> %s\n", interface); if (strcmp(interface, wl_compositor_interface.name) == 0) { compositor = wl_registry_bind(registry, name, &wl_compositor_interface, wl_compositor_interface.version); printf("Got wl_compositor %d\n", version); } else if (strcmp(interface, wl_shm_interface.name) == 0) { shm = wl_registry_bind(registry, name, &wl_shm_interface, wl_shm_interface.version); printf("Got wl_shm %d\n", version); } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { xdg_shell = wl_registry_bind(registry, name, &xdg_wm_base_interface, 4); printf("Got xdg_wm_base %d\n", version); } else if (strcmp(interface, wl_seat_interface.name) == 0) { wl_seat = wl_registry_bind(registry, name, &wl_seat_interface, 5); printf("Got wl_seat (%d)\n", version); } } void registry_global_remove_handler( void *data, struct wl_registry *registry, uint32_t name) { // nothing } const struct wl_registry_listener registry_listener = { .global = registry_global_handler, .global_remove = registry_global_remove_handler}; // xdg_toplevel void xdg_toplevel_configure_handler( void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *states) { printf("xdg_toplevel_configure: %dx%d\n", width, height); } void xdg_toplevel_close_handler(void *data, struct xdg_toplevel *xdg_toplevel) { printf("xdg_toplevel_close\n"); running = 0; } void xdg_toplevel_configure_bounds_handler( void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height) { // nothing } const struct xdg_toplevel_listener xdg_toplevel_listener = { .configure = xdg_toplevel_configure_handler, .close = xdg_toplevel_close_handler, .configure_bounds = xdg_toplevel_configure_bounds_handler}; // xdg_surface void xdg_surface_configure_handler(void *data, struct xdg_surface *xdg_surface, uint32_t serial) { printf("xdg_surface_configure\n"); xdg_surface_ack_configure(xdg_surface, serial); } const struct xdg_surface_listener xdg_surface_listener = { .configure = xdg_surface_configure_handler}; // xdg_shell void xdg_wm_base_ping_handler(void *data, struct xdg_wm_base *xdg_shell, uint32_t serial) { xdg_wm_base_pong(xdg_shell, serial); // printf("wm_base ping-pong\n"); } const struct xdg_wm_base_listener xdg_shell_listener = { .ping = xdg_wm_base_ping_handler}; void wl_buffer_release_handler(void *data, struct wl_buffer *wl_buffer) { // printf("wl_buffer_release\n"); released = 1; } const struct wl_buffer_listener wl_buffer_listener = { .release = wl_buffer_release_handler}; void redraw(); // forward declaration void wl_callack_done(void *data, struct wl_callback *callback, uint32_t time) { if (canvas.frame) { wl_callback_destroy(canvas.frame); canvas.frame = NULL; } if (released) { redraw(); released = 0; } } const struct wl_callback_listener wl_surface_frame_listener = { .done = wl_callack_done}; int main(void) { clock_gettime(CLOCK_MONOTONIC, &mytime); 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); xdg_wm_base_add_listener(xdg_shell, &xdg_shell_listener, NULL); canvas.surface = wl_compositor_create_surface(compositor); struct xdg_surface *xdg_surface = xdg_wm_base_get_xdg_surface(xdg_shell, canvas.surface); struct xdg_toplevel *xdg_toplevel = xdg_surface_get_toplevel(xdg_surface); xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL); xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL); xdg_toplevel_set_app_id(xdg_toplevel, "eu.ar76.wlgol"); xdg_toplevel_set_title(xdg_toplevel, "Game Of Life."); // signal that the surface is ready to be configured wl_surface_commit(canvas.surface); keyboard_data.keyboard = wl_seat_get_keyboard(wl_seat); wl_keyboard_add_listener(keyboard_data.keyboard, &keyboard_listener, &keyboard_data); pointer_init_cursor(&pointer_data, compositor, shm); pointer_data.pointer = wl_seat_get_pointer(wl_seat); wl_pointer_add_listener(pointer_data.pointer, &pointer_listener, &pointer_data); canvas.width = 400; canvas.height = 400; canvas.stride = canvas.width * 4; canvas.size = canvas.stride * canvas.height; // bytes // open an anonymous file and write some zero bytes to it int fd = syscall(SYS_memfd_create, "buffer", 0); ftruncate(fd, canvas.size); // map it to the memory canvas.data = mmap(NULL, canvas.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, canvas.size); close(fd); // mapped, so can be closed // allocate the buffer in that pool canvas.buffer = wl_shm_pool_create_buffer( pool, 0, canvas.width, canvas.height, canvas.stride, WL_SHM_FORMAT_ARGB8888); wl_buffer_add_listener(canvas.buffer, &wl_buffer_listener, NULL); // wait for the surface to be configured wl_display_roundtrip(display); redraw(); while (wl_display_dispatch(display) != -1 && running) { // nothing inside } xdg_toplevel_destroy(xdg_toplevel); xdg_surface_destroy(xdg_surface); wl_surface_destroy(canvas.surface); wl_shm_pool_destroy(pool); wl_registry_destroy(registry); wl_display_disconnect(display); } unsigned char mycolour = 0; char direction = 1; int framecounter = 0; void redraw() { clock_t start = clock(); mycolour += direction; switch (mycolour) { case 0: // lower limit, start to increase direction = 1; break; case 255: // upper limit, start to decrease direction = -1; break; } // printf("dred=%d\n", dred); // draw into buffer for (int x = 0; x < canvas.width; x++) { for (int y = 0; y < canvas.height; y++) { struct pixel { // little-endian ARGB unsigned char blue; unsigned char green; unsigned char red; unsigned char alpha; } *px = (struct pixel *)(canvas.data + y * canvas.stride + x * 4); // draw a stripes pattern if ((x + y) % 30 < 10) { // transparent px->alpha = 0; } else if ((x + y) % 30 < 20) { // yellow px->alpha = 255; px->red = 255; px->green = 255; px->blue = 0; } else { // semitransparent red px->alpha = 128; px->red = mycolour; px->green = 0; px->blue = (255 - mycolour); } } } // callback canvas.frame = wl_surface_frame(canvas.surface); wl_callback_add_listener(canvas.frame, &wl_surface_frame_listener, NULL); wl_surface_attach(canvas.surface, canvas.buffer, 0, 0); wl_surface_damage(canvas.surface, 0, 0, canvas.width, canvas.height); wl_surface_commit(canvas.surface); framecounter++; struct timespec ntime; clock_gettime(CLOCK_MONOTONIC, &ntime); double elapsed_time = (ntime.tv_sec - mytime.tv_sec) + (ntime.tv_nsec - mytime.tv_nsec) / 1e9; if (elapsed_time > 1.0) { clock_t end = clock(); double dur = ((double)end - start) / CLOCKS_PER_SEC; printf("FPS: %d in %g sec\n", framecounter, dur); mytime = ntime; framecounter = 0; } }