diff options
author | Jari Vetoniemi <mailroxas@gmail.com> | 2014-10-25 01:38:30 +0300 |
---|---|---|
committer | Jari Vetoniemi <mailroxas@gmail.com> | 2014-10-25 01:38:30 +0300 |
commit | 3f5e21a83f525586d1ee5b1bcd04cc2e50350403 (patch) | |
tree | 226cb57348ae89bc62df34cfd7ee2afe43f03c45 /lib/renderers/wayland/window.c | |
parent | f8d97efb8f1bc5a7e764e1bb14f0e97ac36de68d (diff) | |
download | bemenu-3f5e21a83f525586d1ee5b1bcd04cc2e50350403.tar.gz bemenu-3f5e21a83f525586d1ee5b1bcd04cc2e50350403.tar.bz2 bemenu-3f5e21a83f525586d1ee5b1bcd04cc2e50350403.zip |
Cleanup wayland renderer and plugin support.
Diffstat (limited to 'lib/renderers/wayland/window.c')
-rw-r--r-- | lib/renderers/wayland/window.c | 311 |
1 files changed, 311 insertions, 0 deletions
diff --git a/lib/renderers/wayland/window.c b/lib/renderers/wayland/window.c new file mode 100644 index 0000000..9234ef2 --- /dev/null +++ b/lib/renderers/wayland/window.c @@ -0,0 +1,311 @@ +#define _DEFAULT_SOURCE +#include "internal.h" +#include "wayland.h" + +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <sys/mman.h> + +#define USE_XDG_SHELL false + +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 + if ((fd = mkostemp(tmpname, O_CLOEXEC)) >= 0) + unlink(tmpname); +#else + if ((fd = mkstemp(tmpname)) >= 0) { + fd = set_cloexec_or_close(fd); + unlink(tmpname); + } +#endif + + return fd; +} + +static int +os_create_anonymous_file(off_t size) +{ + static const char template[] = "/bemenu-shared-XXXXXX"; + int fd; + int ret; + + const char *path; + if (!(path = getenv("XDG_RUNTIME_DIR")) || strlen(path) <= 0) { + errno = ENOENT; + return -1; + } + + char *name; + int ts = (path[strlen(path) - 1] == '/'); + if (!(name = bm_dprintf("%s%s%s", path, (ts ? "" : "/"), template))) + return -1; + + fd = create_tmpfile_cloexec(name); + free(name); + + if (fd < 0) + return -1; + +#ifdef HAVE_POSIX_FALLOCATE + if ((ret = posix_fallocate(fd, 0, size)) != 0) { + close(fd); + errno = ret; + return -1; + } +#else + if ((ret = ftruncate(fd, size)) < 0) { + close(fd); + return -1; + } +#endif + + return fd; +} + +static void +buffer_release(void *data, struct wl_buffer *wl_buffer) +{ + (void)wl_buffer; + struct buffer *buffer = data; + buffer->busy = false; +} + +static const struct wl_buffer_listener buffer_listener = { + .release = buffer_release +}; + +static void +destroy_buffer(struct buffer *buffer) +{ + if (buffer->buffer) + wl_buffer_destroy(buffer->buffer); + if (buffer->cairo.cr) + cairo_destroy(buffer->cairo.cr); + if (buffer->cairo.surface) + cairo_surface_destroy(buffer->cairo.surface); + + memset(buffer, 0, sizeof(struct buffer)); +} + +static bool +create_buffer(struct wl_shm *shm, struct buffer *buffer, int32_t width, int32_t height, uint32_t format) +{ + int fd = -1; + struct wl_shm_pool *pool = NULL; + uint32_t stride = width * 4; + uint32_t size = stride * height; + + if ((fd = os_create_anonymous_file(size)) < 0) { + fprintf(stderr, "creating a buffer file for %d B failed: %m\n", size); + return false; + } + + void *data; + if ((data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) { + fprintf(stderr, "mmap failed: %m\n"); + close(fd); + return false; + } + + if (!(pool = wl_shm_create_pool(shm, fd, size))) { + close(fd); + return false; + } + + if (!(buffer->buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, format))) + goto fail; + + wl_shm_pool_destroy(pool); + pool = NULL; + + close(fd); + fd = -1; + + wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer); + + if (!(buffer->cairo.surface = cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, width, height, stride))) + goto fail; + + if (!(buffer->cairo.cr = cairo_create(buffer->cairo.surface))) + goto fail; + + buffer->width = width; + buffer->height = height; + return true; + +fail: + if (fd > -1) + close(fd); + if (pool) + wl_shm_pool_destroy(pool); + destroy_buffer(buffer); + return false; +} + +static struct buffer* +next_buffer(struct window *window) +{ + assert(window); + + struct buffer *buffer = NULL; + for (int32_t i = 0; i < 2; ++i) { + if (window->buffers[i].busy) + continue; + + buffer = &window->buffers[i]; + break; + } + + if (!buffer) + return NULL; + + if (window->width != buffer->width || window->height != buffer->height) + destroy_buffer(buffer); + + if (!buffer->buffer && !create_buffer(window->shm, buffer, window->width, window->height, WL_SHM_FORMAT_ARGB8888)) + return NULL; + + return buffer; +} + +static void +resize(struct window *window, uint32_t width, uint32_t height) +{ + window->width = width; + window->height = height; +} + +static void +shell_surface_ping(void *data, struct wl_shell_surface *surface, uint32_t serial) +{ + (void)data; + wl_shell_surface_pong(surface, serial); +} + +static void +shell_surface_configure(void *data, struct wl_shell_surface *surface, uint32_t edges, int32_t width, int32_t height) +{ + (void)surface, (void)edges; + resize(data, width, height); +} + +static void +shell_surface_popup_done(void *data, struct wl_shell_surface *surface) +{ + (void)data, (void)surface; +} + +static const struct wl_shell_surface_listener shell_surface_listener = { + .ping = shell_surface_ping, + .configure = shell_surface_configure, + .popup_done = shell_surface_popup_done, +}; + +static void +xdg_surface_configure(void *data, struct xdg_surface *surface, int32_t width, int32_t height, struct wl_array *states, uint32_t serial) +{ + (void)states; + resize(data, width, height); + xdg_surface_ack_configure(surface, serial); +} + +static void +xdg_surface_close(void *data, struct xdg_surface *surface) +{ + (void)data, (void)surface; +} + +static const struct xdg_surface_listener xdg_surface_listener = { + .configure = xdg_surface_configure, + .close = xdg_surface_close, +}; + +void +bm_wl_window_render(struct window *window, const struct bm_menu *menu) +{ + assert(window && menu); + + struct buffer *buffer; + if (!(buffer = next_buffer(window))) + return; + + if (window->notify.render) + window->notify.render(&buffer->cairo, buffer->width, buffer->height, menu); + + wl_surface_damage(window->surface, 0, 0, buffer->width, buffer->height); + wl_surface_attach(window->surface, buffer->buffer, 0, 0); + wl_surface_commit(window->surface); + buffer->busy = true; +} + +void +bm_wl_window_destroy(struct window *window) +{ + assert(window); + + for (int32_t i = 0; i < 2; ++i) + destroy_buffer(&window->buffers[i]); + + if (window->xdg_surface) + xdg_surface_destroy(window->xdg_surface); + + if (window->shell_surface) + wl_shell_surface_destroy(window->shell_surface); + + if (window->surface) + wl_surface_destroy(window->surface); +} + +bool +bm_wl_window_create(struct window *window, struct wl_shm *shm, struct wl_shell *shell, struct xdg_shell *xdg_shell, struct wl_surface *surface) +{ + assert(window); + + if (USE_XDG_SHELL && xdg_shell && (window->xdg_surface = xdg_shell_get_xdg_surface(xdg_shell, surface))) { + xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener, window); + xdg_surface_set_title(window->xdg_surface, "bemenu"); + } else if (shell && (window->shell_surface = wl_shell_get_shell_surface(shell, surface))) { + wl_shell_surface_add_listener(window->shell_surface, &shell_surface_listener, window); + wl_shell_surface_set_title(window->shell_surface, "bemenu"); + wl_shell_surface_set_class(window->shell_surface, "bemenu"); + wl_shell_surface_set_toplevel(window->shell_surface); + } else { + return false; + } + + window->width = 800; + window->height = 240; + + window->shm = shm; + window->surface = surface; + wl_surface_damage(surface, 0, 0, window->width, window->height); + return true; +} + +/* vim: set ts=8 sw=4 tw=0 :*/ |