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 :*/ | 
