Refactor into multiple files
This commit is contained in:
parent
28f821f1e0
commit
68019f5ee8
10 changed files with 434 additions and 358 deletions
13
common.h
Normal file
13
common.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
|
||||
#define STR_EQUAL 0
|
||||
|
||||
struct color_argb {
|
||||
double a;
|
||||
double r;
|
||||
double g;
|
||||
double b;
|
||||
};
|
||||
|
||||
#endif // COMMON_H
|
88
draw.c
Normal file
88
draw.c
Normal file
|
@ -0,0 +1,88 @@
|
|||
#include <stdbool.h>
|
||||
#include <math.h>
|
||||
#include <cairo/cairo.h>
|
||||
#include <pango/pangocairo.h>
|
||||
#include "common.h"
|
||||
#include "state.h"
|
||||
|
||||
|
||||
#define float_is_zero(number) (number < 1e-8)
|
||||
|
||||
|
||||
static void
|
||||
set_source_argb_cairo(cairo_t *cairo, struct color_argb *color) {
|
||||
cairo_set_source_rgba(cairo, color->r, color->g, color->b, color->a);
|
||||
}
|
||||
|
||||
static void
|
||||
draw_rounded_rectangle(cairo_t *cr, int x, int y, int width, int height, int radius)
|
||||
{
|
||||
cairo_new_sub_path(cr);
|
||||
cairo_arc(cr, x + radius, y + radius, radius, M_PI, 3*M_PI/2);
|
||||
cairo_arc(cr, x + width - radius, y + radius, radius, 3*M_PI/2, 0);
|
||||
cairo_arc(cr, x + width - radius, y + height - radius, radius, 0, M_PI/2);
|
||||
cairo_arc(cr, x + radius, y + height - radius, radius, M_PI/2, M_PI);
|
||||
cairo_close_path(cr);
|
||||
}
|
||||
|
||||
void
|
||||
draw_frame_into(uint32_t *data, struct user_request *user_request, const int width, const int height)
|
||||
{
|
||||
cairo_surface_t *cairo_target_surface = cairo_image_surface_create_for_data(
|
||||
(unsigned char *)data, CAIRO_FORMAT_ARGB32, width, height, width * 4);
|
||||
cairo_t *cairo = cairo_create(cairo_target_surface);
|
||||
|
||||
/* Draw background */
|
||||
if (!float_is_zero(user_request->backdrop.a)) {
|
||||
set_source_argb_cairo(cairo, &user_request->backdrop);
|
||||
draw_rounded_rectangle(cairo, 0, 0, width, height, user_request->border_radius);
|
||||
cairo_fill(cairo);
|
||||
}
|
||||
|
||||
/* Load and draw PNG */
|
||||
if (strcmp("", user_request->graphics_filename) != STR_EQUAL) {
|
||||
cairo_save(cairo);
|
||||
cairo_surface_t *cairo_png_surface = cairo_image_surface_create_from_png(user_request->graphics_filename);
|
||||
if (cairo_surface_status(cairo_png_surface) != CAIRO_STATUS_SUCCESS) {
|
||||
fprintf(stderr, "Failed to open PNG image.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Set transformations to fit image in box */
|
||||
int img_width = cairo_image_surface_get_width(cairo_png_surface);
|
||||
int img_height = cairo_image_surface_get_height(cairo_png_surface);
|
||||
double width_scale = ((double)width - 2*user_request->padding) / img_width;
|
||||
double height_scale = (((double)height - 2*user_request->padding) * 3 / 4.0) / img_height;
|
||||
double scale = MIN(width_scale, height_scale);
|
||||
if (scale > 1.0) scale = 1.0;
|
||||
cairo_translate(cairo,
|
||||
(width - img_width *scale)/2.0,
|
||||
(3*height/4.0 - img_height*scale)/2.0);
|
||||
cairo_scale(cairo, scale, scale);
|
||||
|
||||
cairo_set_source_surface(cairo, cairo_png_surface, 0.0, 0.0);
|
||||
cairo_paint(cairo);
|
||||
cairo_surface_destroy(cairo_png_surface);
|
||||
cairo_restore(cairo);
|
||||
}
|
||||
|
||||
/* Draw text */
|
||||
if (user_request->text) {
|
||||
PangoLayout *layout = pango_cairo_create_layout(cairo);
|
||||
pango_layout_set_text(layout, user_request->text, -1);
|
||||
pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
|
||||
pango_layout_set_width(layout, (width - 2*user_request->padding) * PANGO_SCALE);
|
||||
PangoFontDescription *desc = pango_font_description_from_string(user_request->font);
|
||||
pango_layout_set_font_description(layout, desc);
|
||||
pango_font_description_free(desc);
|
||||
set_source_argb_cairo(cairo, &user_request->text_color);
|
||||
int text_width = 0, text_height = 0;
|
||||
pango_layout_get_size(layout, &text_width, &text_height);
|
||||
cairo_move_to(cairo, user_request->padding, 3*height/4.0);
|
||||
pango_cairo_show_layout(cairo, layout);
|
||||
g_object_unref(layout);
|
||||
}
|
||||
|
||||
cairo_surface_destroy(cairo_target_surface);
|
||||
cairo_destroy(cairo);
|
||||
}
|
6
draw.h
Normal file
6
draw.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include "state.h"
|
||||
|
||||
void
|
||||
draw_frame_into(uint32_t *data, struct user_request *user_request, const int width, const int height);
|
18
makefile
18
makefile
|
@ -1,4 +1,6 @@
|
|||
# Choose a dedicated build directory as ‘make clean’ will remove it entirely
|
||||
BUILD = build
|
||||
$(shell mkdir -p $(BUILD))
|
||||
|
||||
CFLAGS += -Wall
|
||||
|
||||
|
@ -13,26 +15,24 @@ LDLIBS += $(shell pkg-config --libs wayland-client)
|
|||
|
||||
LDLIBS += -lrt
|
||||
|
||||
$(shell mkdir -p $(BUILD))
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: build/wl-overlay
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD)/*
|
||||
rm -rf $(BUILD)
|
||||
|
||||
xdg-shell-client-protocol.h: /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml
|
||||
$(BUILD)/wlr-layer-shell-protocol.h: wlr-layer-shell-unstable-v1.xml
|
||||
wayland-scanner client-header < "$^" > "$@"
|
||||
build/xdg-shell-protocol.c: /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml
|
||||
$(BUILD)/wlr-layer-shell-protocol.c: wlr-layer-shell-unstable-v1.xml
|
||||
wayland-scanner private-code < "$^" > "$@"
|
||||
|
||||
wlr-layer-shell-protocol.h: wlr-layer-shell-unstable-v1.xml
|
||||
$(BUILD)/xdg-shell-client-protocol.h: /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml
|
||||
wayland-scanner client-header < "$^" > "$@"
|
||||
build/wlr-layer-shell-protocol.c: wlr-layer-shell-unstable-v1.xml
|
||||
$(BUILD)/xdg-shell-protocol.c: /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml
|
||||
wayland-scanner private-code < "$^" > "$@"
|
||||
|
||||
C_FILES = wl-overlay.c build/xdg-shell-protocol.c build/wlr-layer-shell-protocol.c
|
||||
C_FILES = $(wildcard *.c) $(BUILD)/wlr-layer-shell-protocol.c $(BUILD)/xdg-shell-protocol.c
|
||||
|
||||
build/wl-overlay: $(C_FILES) xdg-shell-client-protocol.h wlr-layer-shell-protocol.h
|
||||
$(BUILD)/wl-overlay: $(C_FILES) $(BUILD)/wlr-layer-shell-protocol.h
|
||||
$(CC) $(CFLAGS) $(INC) $(LDFLAGS) $(LDLIBS) -o "$@" $(C_FILES)
|
||||
|
|
53
shm.c
Normal file
53
shm.c
Normal file
|
@ -0,0 +1,53 @@
|
|||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <time.h>
|
||||
#include <unistd.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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
9
shm.h
Normal file
9
shm.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#ifndef SHM_H
|
||||
#define SHM_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
int
|
||||
allocate_shm_file(size_t size);
|
||||
|
||||
#endif // SHM_H
|
37
state.h
Normal file
37
state.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
#ifndef STATE_H
|
||||
#define STATE_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
struct user_request {
|
||||
char *graphics_filename;
|
||||
char *text;
|
||||
char *font;
|
||||
struct color_argb backdrop;
|
||||
struct color_argb text_color;
|
||||
int border_radius;
|
||||
int width;
|
||||
int height;
|
||||
int padding;
|
||||
};
|
||||
|
||||
struct wayland_state {
|
||||
/* Globals */
|
||||
struct wl_registry *wl_registry;
|
||||
struct wl_display *wl_display;
|
||||
struct wl_shm *wl_shm;
|
||||
struct wl_compositor *wl_compositor;
|
||||
struct zwlr_layer_shell_v1 *layer_shell;
|
||||
|
||||
struct wl_seat *wl_seat;
|
||||
/* Objects */
|
||||
struct wl_surface *wl_surface;
|
||||
struct zwlr_layer_surface_v1 *zwlr_surface;
|
||||
};
|
||||
|
||||
struct client_state {
|
||||
struct user_request user_request;
|
||||
struct wayland_state wl_state;
|
||||
};
|
||||
|
||||
#endif // STATE_H
|
202
wayland.c
Normal file
202
wayland.c
Normal file
|
@ -0,0 +1,202 @@
|
|||
#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 <getopt.h>
|
||||
#include <cairo/cairo.h>
|
||||
#include <pango/pangocairo.h>
|
||||
|
||||
#include "build/wlr-layer-shell-protocol.h"
|
||||
#include "common.h"
|
||||
#include "wayland.h"
|
||||
#include "shm.h"
|
||||
#include "draw.h"
|
||||
|
||||
|
||||
/* Wayland code */
|
||||
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 *
|
||||
create_and_draw_buffer(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_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);
|
||||
|
||||
draw_frame_into(data, &state->user_request, width, height);
|
||||
|
||||
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 = create_and_draw_buffer(state, width, height);
|
||||
wl_surface_attach(state->wl_state.wl_surface, buffer, 0, 0);
|
||||
wl_surface_commit(state->wl_state.wl_surface);
|
||||
}
|
||||
|
||||
static const struct zwlr_layer_surface_v1_listener zwlr_layer_surface_listener = {
|
||||
.configure = zwlr_layer_surface_configure,
|
||||
};
|
||||
|
||||
static void
|
||||
wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t serial, uint32_t time, uint32_t button, uint32_t button_state)
|
||||
{
|
||||
struct client_state *state = data;
|
||||
|
||||
if (state->wl_state.wl_surface != NULL) {
|
||||
wl_surface_destroy(state->wl_state.wl_surface);
|
||||
state->wl_state.wl_surface = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
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) {}
|
||||
void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t serial, struct wl_surface *surface) {}
|
||||
void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {}
|
||||
void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t time, uint32_t axis, wl_fixed_t value) {}
|
||||
void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) {}
|
||||
void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t axis_source) {}
|
||||
void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t time, uint32_t axis) {}
|
||||
void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t axis, int32_t discrete) {}
|
||||
void wl_pointer_axis_value120(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t axis, int32_t value120) {}
|
||||
|
||||
|
||||
static const struct wl_pointer_listener wl_pointer_listener = {
|
||||
.button = wl_pointer_button,
|
||||
.enter = wl_pointer_enter,
|
||||
.leave = wl_pointer_leave,
|
||||
.motion = wl_pointer_motion,
|
||||
.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,
|
||||
.axis_value120 = wl_pointer_axis_value120,
|
||||
};
|
||||
|
||||
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) == STR_EQUAL) {
|
||||
state->wl_state.wl_shm = wl_registry_bind(
|
||||
wl_registry, name, &wl_shm_interface, 1);
|
||||
|
||||
} else if (strcmp(interface, wl_compositor_interface.name) == STR_EQUAL) {
|
||||
state->wl_state.wl_compositor = wl_registry_bind(
|
||||
wl_registry, name, &wl_compositor_interface, 4);
|
||||
|
||||
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == STR_EQUAL) {
|
||||
state->wl_state.layer_shell = wl_registry_bind(
|
||||
wl_registry, name, &zwlr_layer_shell_v1_interface, 1);
|
||||
fprintf(stderr, "Registered global %s, bound address\n", interface);
|
||||
printf("%lx\n", (unsigned long int)state->wl_state.layer_shell);
|
||||
fprintf(stderr, "Stored into state\n");
|
||||
printf("%lx\n", (unsigned long int)state);
|
||||
|
||||
} else if (strcmp(interface, wl_seat_interface.name) == STR_EQUAL) {
|
||||
state->wl_state.wl_seat = wl_registry_bind(
|
||||
wl_registry, name, &wl_seat_interface, 7);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
|
||||
|
||||
void wlo_wl_main(struct client_state *state) {
|
||||
state->wl_state.wl_display = wl_display_connect(NULL);
|
||||
if (!state->wl_state.wl_display) {
|
||||
fprintf(stderr, "Could not connect to Wayland server\n");
|
||||
exit(1);
|
||||
}
|
||||
state->wl_state.wl_registry = wl_display_get_registry(state->wl_state.wl_display);
|
||||
wl_registry_add_listener(state->wl_state.wl_registry, &wl_registry_listener, state);
|
||||
wl_display_roundtrip(state->wl_state.wl_display);
|
||||
|
||||
if (state->wl_state.layer_shell == NULL) {
|
||||
fprintf(stderr, "Wayland compositor does not support wlr-layer-shell protocol\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
state->wl_state.wl_surface = wl_compositor_create_surface(state->wl_state.wl_compositor);
|
||||
state->wl_state.zwlr_surface = zwlr_layer_shell_v1_get_layer_surface(
|
||||
state->wl_state.layer_shell, state->wl_state.wl_surface, NULL, ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "wl-overlay");
|
||||
zwlr_layer_surface_v1_set_size(state->wl_state.zwlr_surface, state->user_request.width, state->user_request.height);
|
||||
zwlr_layer_surface_v1_set_anchor(state->wl_state.zwlr_surface, ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM);
|
||||
zwlr_layer_surface_v1_set_margin(state->wl_state.zwlr_surface, 50, 50, 50, 50);
|
||||
|
||||
zwlr_layer_surface_v1_add_listener(state->wl_state.zwlr_surface, &zwlr_layer_surface_listener, state);
|
||||
|
||||
struct wl_pointer *pointer = wl_seat_get_pointer(state->wl_state.wl_seat);
|
||||
wl_pointer_add_listener(pointer, &wl_pointer_listener, state);
|
||||
|
||||
wl_surface_commit(state->wl_state.wl_surface);
|
||||
|
||||
while (wl_display_dispatch(state->wl_state.wl_display) && state->wl_state.wl_surface != NULL) {
|
||||
/* This space deliberately left blank */
|
||||
}
|
||||
}
|
9
wayland.h
Normal file
9
wayland.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#ifndef WAYLAND_H
|
||||
#define WAYLAND_H
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include "state.h"
|
||||
|
||||
void wlo_wl_main(struct client_state *state);
|
||||
|
||||
#endif // WAYLAND_H
|
355
wl-overlay.c
355
wl-overlay.c
|
@ -1,18 +1,11 @@
|
|||
#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 <getopt.h>
|
||||
#include <wayland-client.h>
|
||||
#include <cairo/cairo.h>
|
||||
#include <pango/pangocairo.h>
|
||||
#include "wlr-layer-shell-protocol.h"
|
||||
#include "common.h"
|
||||
#include "wayland.h"
|
||||
|
||||
#define STR_EQUAL 0
|
||||
|
||||
|
@ -24,67 +17,7 @@
|
|||
#define DEFAULT_BACKDROP_COLOR \
|
||||
((struct color_argb){0.85, 0x11 / 255.0, 0x11 / 255.0, 0x11 / 255.0})
|
||||
|
||||
/* 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 color_argb {
|
||||
double a;
|
||||
double r;
|
||||
double g;
|
||||
double b;
|
||||
};
|
||||
static struct color_argb color_transparent_black = { 0.0, 0.0, 0.0, 0.0 };
|
||||
|
||||
static bool
|
||||
color_argb_equal(struct color_argb *a, struct color_argb *b)
|
||||
{
|
||||
return a->a == b->a && a->r == b->r && a->g == b->g && a->b == b->b;
|
||||
}
|
||||
|
||||
static bool
|
||||
color_argb_invalid(struct color_argb *a)
|
||||
|
@ -97,247 +30,6 @@ color_argb_invalid(struct color_argb *a)
|
|||
);
|
||||
}
|
||||
|
||||
static void
|
||||
set_source_argb_cairo(cairo_t *cairo, struct color_argb *color) {
|
||||
cairo_set_source_rgba(cairo, color->r, color->g, color->b, color->a);
|
||||
}
|
||||
|
||||
struct user_request {
|
||||
char *graphics_filename;
|
||||
char *text;
|
||||
char *font;
|
||||
struct color_argb backdrop;
|
||||
struct color_argb text_color;
|
||||
int border_radius;
|
||||
int width;
|
||||
int height;
|
||||
int padding;
|
||||
};
|
||||
|
||||
struct client_state {
|
||||
struct user_request user_request;
|
||||
bool stop;
|
||||
|
||||
/* Globals */
|
||||
struct wl_registry *wl_registry;
|
||||
struct wl_display *wl_display;
|
||||
struct wl_shm *wl_shm;
|
||||
struct wl_compositor *wl_compositor;
|
||||
struct zwlr_layer_shell_v1 *layer_shell;
|
||||
|
||||
struct wl_seat *wl_seat;
|
||||
/* Objects */
|
||||
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 void
|
||||
draw_rounded_rectangle(cairo_t *cr, int x, int y, int width, int height, int radius)
|
||||
{
|
||||
cairo_new_sub_path(cr);
|
||||
cairo_arc(cr, x + radius, y + radius, radius, M_PI, 3*M_PI/2);
|
||||
cairo_arc(cr, x + width - radius, y + radius, radius, 3*M_PI/2, 0);
|
||||
cairo_arc(cr, x + width - radius, y + height - radius, radius, 0, M_PI/2);
|
||||
cairo_arc(cr, x + radius, y + height - radius, radius, M_PI/2, M_PI);
|
||||
cairo_close_path(cr);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
/* Draw background */
|
||||
if (!color_argb_equal(&state->user_request.backdrop, &color_transparent_black)) {
|
||||
set_source_argb_cairo(cairo, &state->user_request.backdrop);
|
||||
draw_rounded_rectangle(cairo, 0, 0, width, height, state->user_request.border_radius);
|
||||
cairo_fill(cairo);
|
||||
}
|
||||
|
||||
/* Load and draw PNG */
|
||||
if (strcmp("", state->user_request.graphics_filename) != STR_EQUAL) {
|
||||
cairo_save(cairo);
|
||||
cairo_surface_t *cairo_png_surface = cairo_image_surface_create_from_png(state->user_request.graphics_filename);
|
||||
if (cairo_surface_status(cairo_png_surface) != CAIRO_STATUS_SUCCESS) {
|
||||
fprintf(stderr, "Failed to open PNG image.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Set transformations to fit image in box */
|
||||
int img_width = cairo_image_surface_get_width(cairo_png_surface);
|
||||
int img_height = cairo_image_surface_get_height(cairo_png_surface);
|
||||
double width_scale = ((double)width - 2*state->user_request.padding) / img_width;
|
||||
double height_scale = (((double)height - 2*state->user_request.padding) * 3 / 4.0) / img_height;
|
||||
double scale = MIN(width_scale, height_scale);
|
||||
if (scale > 1.0) scale = 1.0;
|
||||
cairo_translate(cairo,
|
||||
(width - img_width *scale)/2.0,
|
||||
(3*height/4.0 - img_height*scale)/2.0);
|
||||
cairo_scale(cairo, scale, scale);
|
||||
|
||||
cairo_set_source_surface(cairo, cairo_png_surface, 0.0, 0.0);
|
||||
cairo_paint(cairo);
|
||||
cairo_surface_destroy(cairo_png_surface);
|
||||
cairo_restore(cairo);
|
||||
}
|
||||
|
||||
/* Draw text */
|
||||
if (state->user_request.text) {
|
||||
PangoLayout *layout = pango_cairo_create_layout(cairo);
|
||||
pango_layout_set_text(layout, state->user_request.text, -1);
|
||||
pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
|
||||
pango_layout_set_width(layout, (width - 2*state->user_request.padding) * PANGO_SCALE);
|
||||
PangoFontDescription *desc = pango_font_description_from_string(
|
||||
state->user_request.font != NULL ? state->user_request.font : DEFAULT_FONT);
|
||||
pango_layout_set_font_description(layout, desc);
|
||||
pango_font_description_free(desc);
|
||||
set_source_argb_cairo(cairo, &state->user_request.text_color);
|
||||
int text_width = 0, text_height = 0;
|
||||
pango_layout_get_size(layout, &text_width, &text_height);
|
||||
cairo_move_to(cairo, state->user_request.padding, 3*height/4.0);
|
||||
pango_cairo_show_layout(cairo, layout);
|
||||
g_object_unref(layout);
|
||||
}
|
||||
|
||||
memcpy(data, cairo_image_surface_get_data(cairo_target_surface), size);
|
||||
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
|
||||
wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t serial, uint32_t time, uint32_t button, uint32_t button_state)
|
||||
{
|
||||
struct client_state *state = data;
|
||||
state->stop = true;
|
||||
}
|
||||
|
||||
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) {}
|
||||
void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t serial, struct wl_surface *surface) {}
|
||||
void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {}
|
||||
void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t time, uint32_t axis, wl_fixed_t value) {}
|
||||
void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) {}
|
||||
void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t axis_source) {}
|
||||
void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t time, uint32_t axis) {}
|
||||
void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t axis, int32_t discrete) {}
|
||||
void wl_pointer_axis_value120(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t axis, int32_t value120) {}
|
||||
|
||||
|
||||
static const struct wl_pointer_listener wl_pointer_listener = {
|
||||
.button = wl_pointer_button,
|
||||
.enter = wl_pointer_enter,
|
||||
.leave = wl_pointer_leave,
|
||||
.motion = wl_pointer_motion,
|
||||
.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,
|
||||
.axis_value120 = wl_pointer_axis_value120,
|
||||
};
|
||||
|
||||
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) == STR_EQUAL) {
|
||||
state->wl_shm = wl_registry_bind(
|
||||
wl_registry, name, &wl_shm_interface, 1);
|
||||
|
||||
} else if (strcmp(interface, wl_compositor_interface.name) == STR_EQUAL) {
|
||||
state->wl_compositor = wl_registry_bind(
|
||||
wl_registry, name, &wl_compositor_interface, 4);
|
||||
|
||||
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == STR_EQUAL) {
|
||||
state->layer_shell = wl_registry_bind(
|
||||
wl_registry, name, &zwlr_layer_shell_v1_interface, 1);
|
||||
|
||||
} else if (strcmp(interface, wl_seat_interface.name) == STR_EQUAL) {
|
||||
state->wl_seat = wl_registry_bind(
|
||||
wl_registry, name, &wl_seat_interface, 7);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
static void
|
||||
parse_hex(char *hex, struct color_argb *result_color) {
|
||||
if (strlen(hex) != 4 * 2) {
|
||||
|
@ -439,6 +131,10 @@ main(int argc, char *argv[])
|
|||
state.user_request.text = NULL;
|
||||
}
|
||||
|
||||
if (state.user_request.font == NULL) {
|
||||
state.user_request.font = strdup(DEFAULT_FONT);
|
||||
}
|
||||
|
||||
if (color_argb_invalid(&state.user_request.backdrop)) {
|
||||
fprintf(stderr, "Invalid --backdrop color, must be hexadecimal aarrggbb\n");
|
||||
exit(1);
|
||||
|
@ -449,51 +145,14 @@ main(int argc, char *argv[])
|
|||
}
|
||||
|
||||
|
||||
wlo_wl_main(&state);
|
||||
|
||||
state.wl_display = wl_display_connect(NULL);
|
||||
if (!state.wl_display) {
|
||||
fprintf(stderr, "Could not connect to Wayland server\n");
|
||||
exit(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);
|
||||
|
||||
if (state.layer_shell == NULL) {
|
||||
fprintf(stderr, "Wayland compositor does not support wlr-layer-shell protocol\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
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, state.user_request.width, state.user_request.height);
|
||||
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);
|
||||
|
||||
struct wl_pointer *pointer = wl_seat_get_pointer(state.wl_seat);
|
||||
wl_pointer_add_listener(pointer, &wl_pointer_listener, &state);
|
||||
|
||||
wl_surface_commit(state.wl_surface);
|
||||
|
||||
while (wl_display_dispatch(state.wl_display) && !state.stop) {
|
||||
/* This space deliberately left blank */
|
||||
}
|
||||
|
||||
if (state.wl_surface != NULL) {
|
||||
wl_surface_destroy(state.wl_surface);
|
||||
state.wl_surface = NULL;
|
||||
}
|
||||
|
||||
free(state.user_request.graphics_filename);
|
||||
if (state.user_request.text != NULL) {
|
||||
free(state.user_request.text);
|
||||
}
|
||||
if (state.user_request.font != NULL) {
|
||||
free(state.user_request.font);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue