wl-overlay/wl-overlay.c
2022-09-07 00:00:55 +02:00

220 lines
5.8 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#include <wayland-client.h>
#include <cairo/cairo.h>
#include "wlr-layer-shell-protocol.h"
/* Shared memory support code */
static void
randname(char *buf)
{
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
long r = ts.tv_nsec;
for (int i = 0; i < 6; ++i) {
buf[i] = 'A'+(r&15)+(r&16)*2;
r >>= 5;
}
}
static int
create_shm_file(void)
{
int retries = 100;
do {
char name[] = "/wl_shm-XXXXXX";
randname(name + sizeof(name) - 7);
--retries;
int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
if (fd >= 0) {
shm_unlink(name);
return fd;
}
} while (retries > 0 && errno == EEXIST);
return -1;
}
static int
allocate_shm_file(size_t size)
{
int fd = create_shm_file();
if (fd < 0)
return -1;
int ret;
do {
ret = ftruncate(fd, size);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
close(fd);
return -1;
}
return fd;
}
/* Wayland code */
struct client_state {
char *filename;
/* Globals */
struct wl_display *wl_display;
struct wl_registry *wl_registry;
struct wl_shm *wl_shm;
struct wl_compositor *wl_compositor;
/* Objects */
struct zwlr_layer_shell_v1 *layer_shell;
struct wl_surface *wl_surface;
struct zwlr_layer_surface_v1 *zwlr_surface;
};
static void
wl_buffer_release(void *data, struct wl_buffer *wl_buffer)
{
/* Sent by the compositor when it's no longer using this buffer */
wl_buffer_destroy(wl_buffer);
}
static const struct wl_buffer_listener wl_buffer_listener = {
.release = wl_buffer_release,
};
static struct wl_buffer *
draw_frame(struct client_state *state, const int width, const int height)
{
int stride = width * 4;
int size = stride * height;
int fd = allocate_shm_file(size);
if (fd == -1) {
return NULL;
}
uint32_t *data = mmap(NULL, size,
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
close(fd);
return NULL;
}
struct wl_shm_pool *pool = wl_shm_create_pool(state->wl_shm, fd, size);
struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0,
width, height, stride, WL_SHM_FORMAT_ARGB8888);
wl_shm_pool_destroy(pool);
close(fd);
/* Load PNG */
cairo_surface_t *cairo_target_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
cairo_t *cairo = cairo_create(cairo_target_surface);
memset(cairo_image_surface_get_data(cairo_target_surface), 0, size);
cairo_surface_t *cairo_png_surface = cairo_image_surface_create_from_png(state->filename);
if (cairo_surface_status(cairo_png_surface) != CAIRO_STATUS_SUCCESS) {
fprintf(stderr, "Failed to open PNG image.\n");
exit(1);
}
cairo_set_source_surface(cairo, cairo_png_surface, 0.0, 0.0);
cairo_paint(cairo);
memcpy(data, cairo_image_surface_get_data(cairo_target_surface), size);
cairo_surface_destroy(cairo_png_surface);
cairo_surface_destroy(cairo_target_surface);
cairo_destroy(cairo);
munmap(data, size);
wl_buffer_add_listener(buffer, &wl_buffer_listener, NULL);
return buffer;
}
static void
zwlr_layer_surface_configure(void *data,
struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1,
uint32_t serial, uint32_t width, uint32_t height)
{
struct client_state *state = data;
zwlr_layer_surface_v1_ack_configure(zwlr_layer_surface_v1, serial);
struct wl_buffer *buffer = draw_frame(state, width, height);
wl_surface_attach(state->wl_surface, buffer, 0, 0);
wl_surface_commit(state->wl_surface);
}
static const struct zwlr_layer_surface_v1_listener zwlr_layer_surface_listener = {
.configure = zwlr_layer_surface_configure,
};
static void
registry_global(void *data, struct wl_registry *wl_registry,
uint32_t name, const char *interface, uint32_t version)
{
struct client_state *state = data;
if (strcmp(interface, wl_shm_interface.name) == 0) {
state->wl_shm = wl_registry_bind(
wl_registry, name, &wl_shm_interface, 1);
} else if (strcmp(interface, wl_compositor_interface.name) == 0) {
state->wl_compositor = wl_registry_bind(
wl_registry, name, &wl_compositor_interface, 4);
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
state->layer_shell = wl_registry_bind(
wl_registry, name, &zwlr_layer_shell_v1_interface, 1);
}
}
static void
registry_global_remove(void *data,
struct wl_registry *wl_registry, uint32_t name)
{
/* This space deliberately left blank */
}
static const struct wl_registry_listener wl_registry_listener = {
.global = registry_global,
.global_remove = registry_global_remove,
};
int
main(int argc, char *argv[])
{
struct client_state state = { 0 };
if (argc < 2 || strcmp("-h", argv[1]) == 0 || strcmp("--help", argv[1]) == 0) {
fprintf(stderr, "Usage: wl-overlay <image.png>\n\nThe image must be a 256×256 PNG with ARGB.\n");
return 1;
}
state.filename = strdup(argv[1]);
state.wl_display = wl_display_connect(NULL);
if (!state.wl_display) {
fprintf(stderr, "Could not connect to Wayland server\n");
return 1;
}
state.wl_registry = wl_display_get_registry(state.wl_display);
wl_registry_add_listener(state.wl_registry, &wl_registry_listener, &state);
wl_display_roundtrip(state.wl_display);
state.wl_surface = wl_compositor_create_surface(state.wl_compositor);
state.zwlr_surface = zwlr_layer_shell_v1_get_layer_surface(
state.layer_shell, state.wl_surface, NULL, ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "wl-overlay");
zwlr_layer_surface_v1_set_size(state.zwlr_surface, 256, 256);
zwlr_layer_surface_v1_set_anchor(state.zwlr_surface, ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM);
zwlr_layer_surface_v1_set_margin(state.zwlr_surface, 50, 50, 50, 50);
zwlr_layer_surface_v1_add_listener(state.zwlr_surface, &zwlr_layer_surface_listener, &state);
wl_surface_commit(state.wl_surface);
while (wl_display_dispatch(state.wl_display)) {
/* This space deliberately left blank */
}
free(state.filename);
return 0;
}