Modular status panel for X11 and Wayland, inspired by https://github.com/jaagr/polybar
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

981 lines
28 KiB

#include "wayland.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <poll.h>
#include <sys/mman.h>
#include <linux/memfd.h>
#include <pixman.h>
#include <wayland-client.h>
#include <wayland-cursor.h>
#include <tllist.h>
#include <xdg-output-unstable-v1.h>
#include <wlr-layer-shell-unstable-v1.h>
#define LOG_MODULE "bar:wayland"
#define LOG_ENABLE_DBG 0
#include "../log.h"
#include "../stride.h"
#include "private.h"
struct buffer {
bool busy;
size_t size;
void *mmapped;
struct wl_buffer *wl_buf;
pixman_image_t *pix;
};
struct monitor {
struct wayland_backend *backend;
struct wl_output *output;
struct zxdg_output_v1 *xdg;
char *name;
int x;
int y;
int width_mm;
int height_mm;
int width_px;
int height_px;
int scale;
};
struct wayland_backend {
struct bar *bar;
struct wl_display *display;
struct wl_registry *registry;
struct wl_compositor *compositor;
struct wl_surface *surface;
struct zwlr_layer_shell_v1 *layer_shell;
struct zwlr_layer_surface_v1 *layer_surface;
struct wl_shm *shm;
struct wl_seat *seat;
struct {
struct wl_pointer *pointer;
uint32_t serial;
int x;
int y;
struct wl_surface *surface;
struct wl_cursor_theme *theme;
struct wl_cursor *cursor;
} pointer;
tll(struct monitor) monitors;
const struct monitor *monitor;
int scale;
struct zxdg_output_manager_v1 *xdg_output_manager;
/* TODO: set directly in bar instead */
int width, height;
/* Used to signal e.g. refresh */
int pipe_fds[2];
/* We're already waiting for a frame done callback */
bool render_scheduled;
tll(struct buffer) buffers; /* List of SHM buffers */
struct buffer *next_buffer; /* Bar is rendering to this one */
struct buffer *pending_buffer; /* Finished, but not yet rendered */
void (*bar_expose)(const struct bar *bar);
void (*bar_on_mouse)(struct bar *bar, enum mouse_event event, int x, int y);
};
void *
bar_backend_wayland_new(void)
{
return calloc(1, sizeof(struct wayland_backend));
}
static void
shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
{
//printf("SHM format: 0x%08x\n", format);
}
static const struct wl_shm_listener shm_listener = {
.format = &shm_format,
};
static void
update_cursor_surface(struct wayland_backend *backend)
{
if (backend->pointer.cursor == NULL)
return;
struct wl_cursor_image *image = backend->pointer.cursor->images[0];
wl_surface_set_buffer_scale(backend->pointer.surface, backend->scale);
wl_surface_attach(
backend->pointer.surface, wl_cursor_image_get_buffer(image), 0, 0);
wl_pointer_set_cursor(
backend->pointer.pointer, backend->pointer.serial,
backend->pointer.surface,
image->hotspot_x / backend->scale, image->hotspot_y / backend->scale);
wl_surface_damage_buffer(
backend->pointer.surface, 0, 0, INT32_MAX, INT32_MAX);
wl_surface_commit(backend->pointer.surface);
wl_display_flush(backend->display);
}
static void
wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, struct wl_surface *surface,
wl_fixed_t surface_x, wl_fixed_t surface_y)
{
struct wayland_backend *backend = data;
backend->pointer.serial = serial;
backend->pointer.x = wl_fixed_to_int(surface_x) * backend->scale;
backend->pointer.y = wl_fixed_to_int(surface_y) * backend->scale;
update_cursor_surface(backend);
}
static void
wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, struct wl_surface *surface)
{
}
static void
wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y)
{
struct wayland_backend *backend = data;
backend->pointer.x = wl_fixed_to_int(surface_x) * backend->scale;
backend->pointer.y = wl_fixed_to_int(surface_y) * backend->scale;
backend->bar_on_mouse(
backend->bar, ON_MOUSE_MOTION, backend->pointer.x, backend->pointer.y);
}
static void
wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, uint32_t time, uint32_t button, uint32_t state)
{
if (state != WL_POINTER_BUTTON_STATE_PRESSED)
return;
struct wayland_backend *backend = data;
backend->bar_on_mouse(
backend->bar, ON_MOUSE_CLICK, backend->pointer.x, backend->pointer.y);
}
static void
wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
uint32_t time, uint32_t axis, wl_fixed_t value)
{
}
static void
wl_pointer_frame(void *data, struct wl_pointer *wl_pointer)
{
}
static void
wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer,
uint32_t axis_source)
{
}
static void
wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer,
uint32_t time, uint32_t axis)
{
}
static void
wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
uint32_t axis, int32_t discrete)
{
}
static const struct wl_pointer_listener pointer_listener = {
.enter = wl_pointer_enter,
.leave = wl_pointer_leave,
.motion = wl_pointer_motion,
.button = wl_pointer_button,
.axis = wl_pointer_axis,
.frame = wl_pointer_frame,
.axis_source = wl_pointer_axis_source,
.axis_stop = wl_pointer_axis_stop,
.axis_discrete = wl_pointer_axis_discrete,
};
static void
seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
enum wl_seat_capability caps)
{
struct wayland_backend *backend = data;
if (backend->pointer.pointer != NULL) {
wl_pointer_release(backend->pointer.pointer);
backend->pointer.pointer = NULL;
}
if ((caps & WL_SEAT_CAPABILITY_POINTER)) {
backend->pointer.pointer = wl_seat_get_pointer(wl_seat);
wl_pointer_add_listener(backend->pointer.pointer, &pointer_listener, backend);
}
}
static void
seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name)
{
}
static const struct wl_seat_listener seat_listener = {
.capabilities = seat_handle_capabilities,
.name = seat_handle_name,
};
static void
output_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y,
int32_t physical_width, int32_t physical_height,
int32_t subpixel, const char *make, const char *model,
int32_t transform)
{
struct monitor *mon = data;
mon->width_mm = physical_width;
mon->height_mm = physical_height;
}
static void
output_mode(void *data, struct wl_output *wl_output, uint32_t flags,
int32_t width, int32_t height, int32_t refresh)
{
}
static void
output_done(void *data, struct wl_output *wl_output)
{
}
static void
output_scale(void *data, struct wl_output *wl_output, int32_t factor)
{
struct monitor *mon = data;
mon->scale = factor;
}
static const struct wl_output_listener output_listener = {
.geometry = &output_geometry,
.mode = &output_mode,
.done = &output_done,
.scale = &output_scale,
};
static void
xdg_output_handle_logical_position(void *data,
struct zxdg_output_v1 *xdg_output,
int32_t x, int32_t y)
{
struct monitor *mon = data;
mon->x = x;
mon->y = y;
}
static void
xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output,
int32_t width, int32_t height)
{
struct monitor *mon = data;
mon->width_px = width;
mon->height_px = height;
}
static void
xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output)
{
}
static void
xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output,
const char *name)
{
struct monitor *mon = data;
mon->name = strdup(name);
}
static void
xdg_output_handle_description(void *data, struct zxdg_output_v1 *xdg_output,
const char *description)
{
}
static struct zxdg_output_v1_listener xdg_output_listener = {
.logical_position = xdg_output_handle_logical_position,
.logical_size = xdg_output_handle_logical_size,
.done = xdg_output_handle_done,
.name = xdg_output_handle_name,
.description = xdg_output_handle_description,
};
static bool
verify_iface_version(const char *iface, uint32_t version, uint32_t wanted)
{
if (version >= wanted)
return true;
LOG_ERR("%s: need interface version %u, but compositor only implements %u",
iface, wanted, version);
return false;
}
static void
handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version)
{
struct wayland_backend *backend = data;
if (strcmp(interface, wl_compositor_interface.name) == 0) {
const uint32_t required = 4;
if (!verify_iface_version(interface, version, required))
return;
backend->compositor = wl_registry_bind(
registry, name, &wl_compositor_interface, required);
}
else if (strcmp(interface, wl_shm_interface.name) == 0) {
const uint32_t required = 1;
if (!verify_iface_version(interface, version, required))
return;
backend->shm = wl_registry_bind(
registry, name, &wl_shm_interface, required);
wl_shm_add_listener(backend->shm, &shm_listener, backend);
wl_display_roundtrip(backend->display);
}
else if (strcmp(interface, wl_output_interface.name) == 0) {
const uint32_t required = 3;
if (!verify_iface_version(interface, version, required))
return;
struct wl_output *output = wl_registry_bind(
registry, name, &wl_output_interface, required);
tll_push_back(backend->monitors, ((struct monitor){
.backend = backend,
.output = output}));
struct monitor *mon = &tll_back(backend->monitors);
wl_output_add_listener(output, &output_listener, mon);
/*
* The "output" interface doesn't give us the monitors'
* identifiers (e.g. "LVDS-1"). Use the XDG output interface
* for that.
*/
assert(backend->xdg_output_manager != NULL);
if (backend->xdg_output_manager != NULL) {
mon->xdg = zxdg_output_manager_v1_get_xdg_output(
backend->xdg_output_manager, mon->output);
zxdg_output_v1_add_listener(mon->xdg, &xdg_output_listener, mon);
}
wl_display_roundtrip(backend->display);
}
else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
const uint32_t required = 1;
if (!verify_iface_version(interface, version, required))
return;
backend->layer_shell = wl_registry_bind(
registry, name, &zwlr_layer_shell_v1_interface, required);
}
else if (strcmp(interface, wl_seat_interface.name) == 0) {
const uint32_t required = 3;
if (!verify_iface_version(interface, version, required))
return;
backend->seat = wl_registry_bind(registry, name, &wl_seat_interface, required);
wl_seat_add_listener(backend->seat, &seat_listener, backend);
wl_display_roundtrip(backend->display);
}
else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) {
const uint32_t required = 2;
if (!verify_iface_version(interface, version, required))
return;
backend->xdg_output_manager = wl_registry_bind(
registry, name, &zxdg_output_manager_v1_interface, required);
}
}
static void
handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
{
LOG_WARN("global removed: %u", name);
}
static const struct wl_registry_listener registry_listener = {
.global = &handle_global,
.global_remove = &handle_global_remove,
};
static void
layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface,
uint32_t serial, uint32_t w, uint32_t h)
{
struct wayland_backend *backend = data;
backend->width = w * backend->scale;
backend->height = h * backend->scale;
zwlr_layer_surface_v1_ack_configure(surface, serial);
}
static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
.configure = layer_surface_configure,
};
static void
buffer_release(void *data, struct wl_buffer *wl_buffer)
{
//printf("buffer release\n");
struct buffer *buffer = data;
assert(buffer->busy);
buffer->busy = false;
}
static const struct wl_buffer_listener buffer_listener = {
.release = &buffer_release,
};
static struct buffer *
get_buffer(struct wayland_backend *backend)
{
tll_foreach(backend->buffers, it) {
if (!it->item.busy) {
it->item.busy = true;
return &it->item;
}
}
/*
* No existing buffer available. Create a new one by:
*
* 1. open a memory backed "file" with memfd_create()
* 2. mmap() the memory file, to be used by the pixman image
* 3. create a wayland shm buffer for the same memory file
*
* The pixman image and the wayland buffer are now sharing memory.
*/
int pool_fd = -1;
void *mmapped = NULL;
size_t size = 0;
struct wl_shm_pool *pool = NULL;
struct wl_buffer *buf = NULL;
pixman_image_t *pix = NULL;
/* Backing memory for SHM */
pool_fd = memfd_create("yambar-wayland-shm-buffer-pool", MFD_CLOEXEC);
if (pool_fd == -1) {
LOG_ERRNO("failed to create SHM backing memory file");
goto err;
}
/* Total size */
const uint32_t stride = stride_for_format_and_width(
PIXMAN_a8r8g8b8, backend->width);
size = stride * backend->height;
if (ftruncate(pool_fd, size) == -1) {
LOG_ERR("failed to truncate SHM pool");
goto err;
}
mmapped = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, pool_fd, 0);
if (mmapped == MAP_FAILED) {
LOG_ERR("failed to mmap SHM backing memory file");
goto err;
}
pool = wl_shm_create_pool(backend->shm, pool_fd, size);
if (pool == NULL) {
LOG_ERR("failed to create SHM pool");
goto err;
}
buf = wl_shm_pool_create_buffer(
pool, 0, backend->width, backend->height, stride, WL_SHM_FORMAT_ARGB8888);
if (buf == NULL) {
LOG_ERR("failed to create SHM buffer");
goto err;
}
/* We use the entire pool for our single buffer */
wl_shm_pool_destroy(pool); pool = NULL;
close(pool_fd); pool_fd = -1;
pix = pixman_image_create_bits_no_clear(
PIXMAN_a8r8g8b8, backend->width, backend->height, (uint32_t *)mmapped, stride);
if (pix == NULL) {
LOG_ERR("failed to create pixman image");
goto err;
}
/* Push to list of available buffers, but marked as 'busy' */
tll_push_back(
backend->buffers,
((struct buffer){
.busy = true,
.size = size,
.mmapped = mmapped,
.wl_buf = buf,
.pix = pix,
})
);
struct buffer *ret = &tll_back(backend->buffers);
wl_buffer_add_listener(ret->wl_buf, &buffer_listener, ret);
return ret;
err:
if (pix != NULL)
pixman_image_unref(pix);
if (buf != NULL)
wl_buffer_destroy(buf);
if (pool != NULL)
wl_shm_pool_destroy(pool);
if (pool_fd != -1)
close(pool_fd);
if (mmapped != NULL)
munmap(mmapped, size);
return NULL;
}
static bool
setup(struct bar *_bar)
{
struct private *bar = _bar->private;
struct wayland_backend *backend = bar->backend.data;
backend->bar = _bar;
backend->display = wl_display_connect(NULL);
if (backend->display == NULL) {
LOG_ERR("failed to connect to wayland; no compistor running?");
return false;
}
backend->registry = wl_display_get_registry(backend->display);
if (backend->registry == NULL) {
LOG_ERR("failed to get wayland registry");
return false;
}
wl_registry_add_listener(backend->registry, &registry_listener, backend);
wl_display_roundtrip(backend->display);
if (backend->compositor == NULL) {
LOG_ERR("no compositor");
return false;
}
if (backend->layer_shell == NULL) {
LOG_ERR("no layer shell interface");
return false;
}
if (backend->shm == NULL) {
LOG_ERR("no shared memory buffers interface");
return false;
}
if (tll_length(backend->monitors) == 0) {
LOG_ERR("no monitors");
return false;
}
tll_foreach(backend->monitors, it) {
const struct monitor *mon = &it->item;
LOG_INFO("monitor: %s: %dx%d+%d+%d (%dx%dmm)",
mon->name, mon->width_px, mon->height_px,
mon->x, mon->y, mon->width_mm, mon->height_mm);
if (bar->monitor != NULL && strcmp(bar->monitor, mon->name) == 0) {
/* User specified a monitor, and this is one */
backend->monitor = mon;
}
}
backend->scale = backend->monitor != NULL ? backend->monitor->scale : 1;
backend->surface = wl_compositor_create_surface(backend->compositor);
if (backend->surface == NULL) {
LOG_ERR("failed to create panel surface");
return false;
}
backend->pointer.surface = wl_compositor_create_surface(backend->compositor);
if (backend->pointer.surface == NULL) {
LOG_ERR("failed to create cursor surface");
return false;
}
unsigned cursor_size = 24;
const char *cursor_theme = getenv("XCURSOR_THEME");
{
const char *env_cursor_size = getenv("XCURSOR_SIZE");
if (env_cursor_size != NULL) {
unsigned size;
if (sscanf(env_cursor_size, "%u", &size) == 1)
cursor_size = size;
}
}
LOG_INFO("cursor theme: %s, size: %u", cursor_theme, cursor_size);
backend->pointer.theme = wl_cursor_theme_load(
cursor_theme, cursor_size * backend->scale, backend->shm);
if (backend->pointer.theme == NULL) {
LOG_ERR("failed to load cursor theme");
return false;
}
backend->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
backend->layer_shell, backend->surface,
backend->monitor != NULL ? backend->monitor->output : NULL,
ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "yambar");
if (backend->layer_surface == NULL) {
LOG_ERR("failed to create layer shell surface");
return false;
}
/* Aligned to top, maximum width */
enum zwlr_layer_surface_v1_anchor top_or_bottom = bar->location == BAR_TOP
? ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
: ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
zwlr_layer_surface_v1_set_anchor(
backend->layer_surface,
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
top_or_bottom);
int height = bar->height_with_border;
height /= backend->scale;
height *= backend->scale;
bar->height = height - 2 * bar->border.width;
bar->height_with_border = height;
zwlr_layer_surface_v1_set_size(
backend->layer_surface, 0, bar->height_with_border / backend->scale);
zwlr_layer_surface_v1_set_exclusive_zone(
backend->layer_surface,
(bar->height_with_border + (bar->location == BAR_TOP
? bar->border.bottom_margin
: bar->border.top_margin))
/ backend->scale);
zwlr_layer_surface_v1_set_margin(
backend->layer_surface,
bar->border.top_margin / backend->scale,
bar->border.right_margin / backend->scale,
bar->border.bottom_margin / backend->scale,
bar->border.left_margin / backend->scale
);
zwlr_layer_surface_v1_add_listener(
backend->layer_surface, &layer_surface_listener, backend);
/* Trigger a 'configure' event, after which we'll have the width */
wl_surface_commit(backend->surface);
wl_display_roundtrip(backend->display);
if (backend->width == -1 ||
backend->height != bar->height_with_border) {
LOG_ERR("failed to get panel width");
return false;
}
assert(backend->monitor == NULL ||
backend->width / backend->monitor->scale <= backend->monitor->width_px);
bar->width = backend->width;
if (pipe(backend->pipe_fds) == -1) {
LOG_ERRNO("failed to create pipe");
return false;
}
backend->render_scheduled = false;
/* Prepare a buffer + pixman image for bar to draw to */
backend->next_buffer = get_buffer(backend);
assert(backend->next_buffer != NULL && backend->next_buffer->busy);
bar->pix = backend->next_buffer->pix;
return true;
}
static void
cleanup(struct bar *_bar)
{
struct private *bar = _bar->private;
struct wayland_backend *backend = bar->backend.data;
tll_foreach(backend->buffers, it) {
if (it->item.wl_buf != NULL)
wl_buffer_destroy(it->item.wl_buf);
if (it->item.pix != NULL)
pixman_image_unref(it->item.pix);
munmap(it->item.mmapped, it->item.size);
tll_remove(backend->buffers, it);
}
tll_foreach(backend->monitors, it) {
struct monitor *mon = &it->item;
free(mon->name);
if (mon->xdg != NULL)
zxdg_output_v1_destroy(mon->xdg);
if (mon->output != NULL)
wl_output_destroy(mon->output);
tll_remove(backend->monitors, it);
}
if (backend->xdg_output_manager != NULL)
zxdg_output_manager_v1_destroy(backend->xdg_output_manager);
/* TODO: move to bar */
free(bar->cursor_name);
if (backend->layer_surface != NULL)
zwlr_layer_surface_v1_destroy(backend->layer_surface);
if (backend->layer_shell != NULL)
zwlr_layer_shell_v1_destroy(backend->layer_shell);
if (backend->pointer.theme != NULL)
wl_cursor_theme_destroy(backend->pointer.theme);
if (backend->pointer.pointer != NULL)
wl_pointer_destroy(backend->pointer.pointer);
if (backend->pointer.surface != NULL)
wl_surface_destroy(backend->pointer.surface);
if (backend->surface != NULL)
wl_surface_destroy(backend->surface);
if (backend->seat != NULL)
wl_seat_destroy(backend->seat);
if (backend->compositor != NULL)
wl_compositor_destroy(backend->compositor);
if (backend->shm != NULL)
wl_shm_destroy(backend->shm);
if (backend->registry != NULL)
wl_registry_destroy(backend->registry);
if (backend->display != NULL)
wl_display_disconnect(backend->display);
/* Destroyed when freeing buffer list */
bar->pix = NULL;
}
static void
loop(struct bar *_bar,
void (*expose)(const struct bar *bar),
void (*on_mouse)(struct bar *bar, enum mouse_event event, int x, int y))
{
struct private *bar = _bar->private;
struct wayland_backend *backend = bar->backend.data;
backend->bar_expose = expose;
backend->bar_on_mouse = on_mouse;
while (wl_display_prepare_read(backend->display) != 0)
wl_display_dispatch_pending(backend->display);
wl_display_flush(backend->display);
while (true) {
struct pollfd fds[] = {
{.fd = _bar->abort_fd, .events = POLLIN},
{.fd = wl_display_get_fd(backend->display), .events = POLLIN},
{.fd = backend->pipe_fds[0], .events = POLLIN},
};
poll(fds, sizeof(fds) / sizeof(fds[0]), -1);
if (fds[0].revents & POLLIN) {
break;
}
if (fds[1].revents & POLLHUP) {
LOG_INFO("disconnected from wayland");
if (write(_bar->abort_fd, &(uint64_t){1}, sizeof(uint64_t))
!= sizeof(uint64_t))
{
LOG_ERRNO("failed to signal abort to modules");
}
break;
}
if (fds[2].revents & POLLIN) {
uint8_t command;
if (read(backend->pipe_fds[0], &command, sizeof(command))
!= sizeof(command))
{
LOG_ERRNO("failed to read from command pipe");
break;
}
assert(command == 1);
expose(_bar);
}
if (fds[1].revents & POLLIN) {
wl_display_read_events(backend->display);
while (wl_display_prepare_read(backend->display) != 0)
wl_display_dispatch_pending(backend->display);
wl_display_flush(backend->display);
}
}
wl_display_cancel_read(backend->display);
}
static void frame_callback(
void *data, struct wl_callback *wl_callback, uint32_t callback_data);
static const struct wl_callback_listener frame_listener = {
.done = &frame_callback,
};
static void
frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_data)
{
//printf("frame callback\n");
struct private *bar = data;
struct wayland_backend *backend = bar->backend.data;
backend->render_scheduled = false;
wl_callback_destroy(wl_callback);
if (backend->pending_buffer != NULL) {
struct buffer *buffer = backend->pending_buffer;
assert(buffer->busy);
wl_surface_set_buffer_scale(backend->surface, backend->scale);
wl_surface_attach(backend->surface, buffer->wl_buf, 0, 0);
wl_surface_damage(backend->surface, 0, 0, backend->width, backend->height);
struct wl_callback *cb = wl_surface_frame(backend->surface);
wl_callback_add_listener(cb, &frame_listener, bar);
wl_surface_commit(backend->surface);
wl_display_flush(backend->display);
backend->pending_buffer = NULL;
backend->render_scheduled = true;
} else
;//printf("nothing more to do\n");
}
static void
commit(const struct bar *_bar)
{
struct private *bar = _bar->private;
struct wayland_backend *backend = bar->backend.data;
//printf("commit: %dxl%d\n", backend->width, backend->height);
assert(backend->next_buffer != NULL);
assert(backend->next_buffer->busy);
if (backend->render_scheduled) {
//printf("already scheduled\n");
if (backend->pending_buffer != NULL)
backend->pending_buffer->busy = false;
backend->pending_buffer = backend->next_buffer;
backend->next_buffer = NULL;
} else {
//printf("scheduling new frame callback\n");
struct buffer *buffer = backend->next_buffer;
assert(buffer->busy);
wl_surface_set_buffer_scale(backend->surface, backend->scale);
wl_surface_attach(backend->surface, buffer->wl_buf, 0, 0);
wl_surface_damage(backend->surface, 0, 0, backend->width, backend->height);
struct wl_callback *cb = wl_surface_frame(backend->surface);
wl_callback_add_listener(cb, &frame_listener, bar);
wl_surface_commit(backend->surface);
wl_display_flush(backend->display);
backend->render_scheduled = true;
}
backend->next_buffer = get_buffer(backend);
assert(backend->next_buffer != NULL && backend->next_buffer->busy);
bar->pix = backend->next_buffer->pix;
}
static void
refresh(const struct bar *_bar)
{
const struct private *bar = _bar->private;
const struct wayland_backend *backend = bar->backend.data;
if (write(backend->pipe_fds[1], &(uint8_t){1}, sizeof(uint8_t))
!= sizeof(uint8_t))
{
LOG_ERRNO("failed to signal 'refresh' to main thread");
}
}
static void
set_cursor(struct bar *_bar, const char *cursor)
{
struct private *bar = _bar->private;
struct wayland_backend *backend = bar->backend.data;
backend->pointer.cursor = wl_cursor_theme_get_cursor(
backend->pointer.theme, cursor);
update_cursor_surface(backend);
}
const struct backend wayland_backend_iface = {
.setup = &setup,
.cleanup = &cleanup,
.loop = &loop,
.commit = &commit,
.refresh = &refresh,
.set_cursor = &set_cursor,
};