#include #include #include #include #include #include #include #include #include #include #include #include "xdg-shell.h" struct wl_display *display = NULL; struct wl_registry *registry = NULL; struct wl_compositor *compositor = NULL; struct wl_surface *surface = NULL; struct wl_shm *shm = NULL; struct wl_buffer *buffer = NULL; void *shm_data; struct xdg_wm_base *xdg_wm_base = NULL; struct xdg_surface *xdg_surface = NULL; struct xdg_toplevel *xdg_toplevel = NULL; //================= // Shared Memory //================= static int set_cloexec_or_close(int fd) { if (fd == -1) return -1; long flags = fcntl(fd, F_GETFD); if (flags == -1) goto err; if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) goto err; return fd; err: close(fd); return -1; } static int create_tmpfile_cloexec(char *tmpname) { int fd; #ifdef HAVE_MKOSTEMP fd = mkostemp(tmpname, O_CLOEXEC); if (fd >= 0) unlink(tmpname); #else fd = mkstemp(tmpname); if (fd >= 0) { fd = set_cloexec_or_close(fd); unlink(tmpname); } #endif return fd; } /* * Create a new, unique, anonymous file of the given size, and * return the file descriptor for it. The file descriptor is set * CLOEXEC. The file is immediately suitable for mmap()'ing * the given size at offset zero. * * The file should not have a permanent backing store like a disk, * but may have if XDG_RUNTIME_DIR is not properly implemented in OS. * * The file name is deleted from the file system. * * The file is suitable for buffer sharing between processes by * transmitting the file descriptor over Unix sockets using the * SCM_RIGHTS methods. */ int os_create_anonymous_file(off_t size) { static const char template[] = "/a404m-shared-XXXXXX"; const char *path = getenv("XDG_RUNTIME_DIR"); if (!path) { errno = ENOENT; return -1; } char *name = malloc(strlen(path) + sizeof(template)); if (!name) return -1; strcpy(name, path); strcat(name, template); int fd = create_tmpfile_cloexec(name); free(name); if (fd < 0) return -1; if (ftruncate(fd, size) < 0) { close(fd); return -1; } return fd; } //============= // Shm //============= static void shm_format_handler(void *data, struct wl_shm *shm, uint32_t format) { (void)data; (void)shm; fprintf(stderr, "Format %d\n", format); } static const struct wl_shm_listener shm_listener = { .format = shm_format_handler, }; //============== // XDG //============== static void xdg_wm_base_ping_handler(void *data, struct xdg_wm_base *wm_base, uint32_t serial) { (void)data; xdg_wm_base_pong(wm_base, serial); } static const struct xdg_wm_base_listener xdg_wm_base_listener = { .ping = xdg_wm_base_ping_handler, }; static void xdg_surface_configure_handler(void *data, struct xdg_surface *xdg_surface, uint32_t serial) { (void)data; xdg_surface_ack_configure(xdg_surface, serial); } static const struct xdg_surface_listener xdg_surface_listener = { .configure = xdg_surface_configure_handler, }; static void xdg_toplevel_configure_handler(void *data, struct xdg_toplevel *toplevel, int32_t width, int32_t height, struct wl_array *states) { (void)data; (void)toplevel; (void)states; fprintf(stderr, "XDG toplevel configure: %dx%d\n", width, height); } static void xdg_toplevel_close_handler(void *data, struct xdg_toplevel *toplevel) { (void)data; (void)toplevel; } static const struct xdg_toplevel_listener xdg_toplevel_listener = { .configure = xdg_toplevel_configure_handler, .close = xdg_toplevel_close_handler, }; //============== // Global //============== static void global_registry_handler(void *data, struct wl_registry *registry, uint32_t id, const char *interface, uint32_t version) { printf("Got a registry event for <%s>, id: %d, version: %d.\n", interface, id, version); if (strcmp(interface, "wl_compositor") == 0) { compositor = wl_registry_bind(registry, id, &wl_compositor_interface, 4); } else if (strcmp(interface, "xdg_wm_base") == 0) { xdg_wm_base = wl_registry_bind(registry, id, &xdg_wm_base_interface, 1); } else if (strcmp(interface, "wl_shm") == 0) { shm = wl_registry_bind(registry, id, &wl_shm_interface, 1); wl_shm_add_listener(shm, &shm_listener, NULL); } } static void global_registry_remove_handler(void *data, struct wl_registry *registry, uint32_t id) { printf("Got a registry losing event for <%d>\n", id); } static const struct wl_registry_listener registry_listener = { .global = global_registry_handler, .global_remove = global_registry_remove_handler, }; //========== // Buffer //========== static struct wl_buffer *create_buffer(int width, int height) { struct wl_shm_pool *pool; int stride = width * 4; // 4 bytes per pixel in our ARGB8888 format. int size = stride * height; int fd; struct wl_buffer *buff; fd = os_create_anonymous_file(size); if (fd < 0) { fprintf(stderr, "Failed to create a buffer. size: %d\n", size); exit(1); } shm_data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (shm_data == MAP_FAILED) { fprintf(stderr, "mmap failed!\n"); close(fd); exit(1); } pool = wl_shm_create_pool(shm, fd, size); buff = wl_shm_pool_create_buffer(pool, 0, width, height, stride, WL_SHM_FORMAT_ARGB8888); wl_shm_pool_destroy(pool); return buff; } //========= // A404M //========= static const int width = 480, height = 360; static int last_frame = 0; static int cube_pos_x = 0; static int cube_pos_y = 0; static bool cube_flag_x = true; static bool cube_flag_y = true; static constexpr int cube_size = 50; static uint32_t cube_color = 0xff22aaff; static constexpr uint32_t back_color = 0xaa222222; static void collide() { cube_color = (0xff000000 | (0xffffff & rand())); } static void draw() { uint32_t *pixel = shm_data; const int cube_end_pos_x = cube_pos_x + cube_size; const int cube_end_pos_y = cube_pos_y + cube_size; int cube_begin_loop_x = cube_pos_x - 1; int cube_begin_loop_y = cube_pos_y - 1; if (cube_begin_loop_x < 0) { cube_begin_loop_x = 0; } if (cube_begin_loop_y < 0) { cube_begin_loop_y = 0; } int cube_end_loop_x = cube_pos_x + cube_size + 1; int cube_end_loop_y = cube_pos_y + cube_size + 1; if (cube_end_loop_x > width) { cube_end_loop_x = width; } if (cube_end_loop_y > height) { cube_end_loop_y = height; } for (int x = cube_begin_loop_x; x < cube_end_loop_x; ++x) { for (int y = cube_begin_loop_y; y < cube_end_loop_y; ++y) { if (cube_pos_x < x && x < cube_end_pos_x && cube_pos_y < y && y < cube_end_pos_y) { pixel[x + y * width] = cube_color; } else { // *pixel = (rand() << 3 * 8) | (0xffffff & rand()); pixel[x + y * width] = back_color; } } } if (cube_flag_x) { ++cube_pos_x; } else { --cube_pos_x; } if (cube_flag_y) { ++cube_pos_y; } else { --cube_pos_y; } if (cube_pos_x < 0) { cube_flag_x = true; collide(); } else if (cube_pos_x + cube_size >= width) { cube_flag_x = false; collide(); } if (cube_pos_y < 0) { cube_flag_y = true; collide(); } else if (cube_pos_y + cube_size >= height) { cube_flag_y = false; collide(); } wl_surface_attach(surface, buffer, 0, 0); wl_surface_damage_buffer(surface, cube_begin_loop_x, cube_begin_loop_y, cube_end_loop_x, cube_end_loop_y); wl_surface_commit(surface); } static const struct wl_callback_listener surface_frame_listener; static void surface_frame_done(void *data, struct wl_callback *callback, uint32_t time) { // printf("frame %d\n", time - last_frame); last_frame = time; wl_callback_destroy(callback); wl_callback_add_listener(wl_surface_frame(surface), &surface_frame_listener, data); draw(); } static const struct wl_callback_listener surface_frame_listener = { .done = surface_frame_done, }; //========== // Main //========== int main(int argc, char *argv[]) { (void)argc; (void)argv; srand(time(NULL)); cube_pos_x = rand() % width; cube_pos_y = rand() % height; display = wl_display_connect(NULL); if (display == NULL) { fprintf(stderr, "Failed to connect to display.\n"); exit(1); } printf("Connected to display.\n"); registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); wl_display_dispatch(display); wl_display_roundtrip(display); // Check compositor. fprintf(stderr, " - Checking compositor...\n"); if (compositor == NULL) { fprintf(stderr, "Can't find compositor.\n"); exit(1); } else { printf("Found compositor!\n"); } // Surface surface = wl_compositor_create_surface(compositor); if (surface == NULL) { exit(1); } // XDG. xdg_wm_base_add_listener(xdg_wm_base, &xdg_wm_base_listener, NULL); xdg_surface = xdg_wm_base_get_xdg_surface(xdg_wm_base, surface); xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL); xdg_toplevel = xdg_surface_get_toplevel(xdg_surface); xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL); // This step is important on Weston. wl_surface_commit(surface); wl_display_roundtrip(display); struct wl_callback *fcb = wl_surface_frame(surface); wl_callback_add_listener(fcb, &surface_frame_listener, NULL); // Create a buffer. buffer = create_buffer(width, height); wl_surface_attach(surface, buffer, 0, 0); wl_surface_commit(surface); // Drawing pixels. draw(); /* wl_surface_attach(surface, buffer, 0, 0); wl_surface_damage_buffer(surface, 0, 0, width, height); wl_surface_commit(surface); */ // Display loop. while (true) { int cond = wl_display_dispatch(display); // printf("%d\n", cond); if (cond == -1) { break; } } // Free resources. xdg_toplevel_destroy(xdg_toplevel); xdg_surface_destroy(xdg_surface); wl_surface_destroy(surface); wl_display_disconnect(display); printf("Disconnected from display.\n"); return 0; }