Add styling options
This commit is contained in:
parent
bbb8c41f01
commit
d453400ee6
2 changed files with 181 additions and 29 deletions
11
makefile
11
makefile
|
@ -2,19 +2,16 @@ BUILD = build
|
||||||
|
|
||||||
CFLAGS += -Wall
|
CFLAGS += -Wall
|
||||||
|
|
||||||
# Pango and dependencies
|
|
||||||
CFLAGS += $(shell pkg-config --cflags pangocairo)
|
CFLAGS += $(shell pkg-config --cflags pangocairo)
|
||||||
LDLIBS += $(shell pkg-config --libs pangocairo)
|
LDLIBS += $(shell pkg-config --libs pangocairo)
|
||||||
|
|
||||||
#CFLAGS += $(shell pkg-config --cflags glib-2.0)
|
CFLAGS += $(shell pkg-config --cflags cairo)
|
||||||
#LDLIBS += $(shell pkg-config --libs glib-2.0)
|
LDLIBS += $(shell pkg-config --libs cairo)
|
||||||
|
|
||||||
#CFLAGS += $(shell pkg-config --cflags harfbuzz)
|
CFLAGS += $(shell pkg-config --cflags wayland-client)
|
||||||
#LDLIBS += $(shell pkg-config --libs harfbuzz)
|
LDLIBS += $(shell pkg-config --libs wayland-client)
|
||||||
|
|
||||||
LDLIBS += -lwayland-client
|
|
||||||
LDLIBS += -lrt
|
LDLIBS += -lrt
|
||||||
LDLIBS += -lcairo
|
|
||||||
|
|
||||||
$(shell mkdir -p $(BUILD))
|
$(shell mkdir -p $(BUILD))
|
||||||
|
|
||||||
|
|
199
wl-overlay.c
199
wl-overlay.c
|
@ -8,6 +8,7 @@
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <getopt.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <cairo/cairo.h>
|
#include <cairo/cairo.h>
|
||||||
#include <pango/pangocairo.h>
|
#include <pango/pangocairo.h>
|
||||||
|
@ -15,6 +16,14 @@
|
||||||
|
|
||||||
#define STR_EQUAL 0
|
#define STR_EQUAL 0
|
||||||
|
|
||||||
|
#define DEFAULT_BORDER_RADIUS 15
|
||||||
|
#define DEFAULT_MARGIN 10
|
||||||
|
#define DEFAULT_WIDTH 256
|
||||||
|
#define DEFAULT_HEIGHT 256
|
||||||
|
#define DEFAULT_FONT "Fira Sans 17"
|
||||||
|
#define DEFAULT_BACKDROP_COLOR \
|
||||||
|
((struct color_argb){0.85, 0x11 / 255.0, 0x11 / 255.0, 0x11 / 255.0})
|
||||||
|
|
||||||
/* Shared memory support code */
|
/* Shared memory support code */
|
||||||
static void
|
static void
|
||||||
randname(char *buf)
|
randname(char *buf)
|
||||||
|
@ -63,10 +72,46 @@ allocate_shm_file(size_t size)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wayland code */
|
/* 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)
|
||||||
|
{
|
||||||
|
return !(
|
||||||
|
(0.0 <= a->a) && (a->a <= 1.0) &&
|
||||||
|
(0.0 <= a->r) && (a->r <= 1.0) &&
|
||||||
|
(0.0 <= a->g) && (a->g <= 1.0) &&
|
||||||
|
(0.0 <= a->b) && (a->b <= 1.0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
struct user_request {
|
||||||
char *graphics_filename;
|
char *graphics_filename;
|
||||||
char *text;
|
char *text;
|
||||||
bool backdrop;
|
char *font;
|
||||||
|
struct color_argb backdrop;
|
||||||
|
struct color_argb text_color;
|
||||||
|
int border_radius;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int margin;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct client_state {
|
struct client_state {
|
||||||
|
@ -128,40 +173,58 @@ draw_frame(struct client_state *state, const int width, const int height)
|
||||||
wl_shm_pool_destroy(pool);
|
wl_shm_pool_destroy(pool);
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
/* Draw background */
|
|
||||||
cairo_surface_t *cairo_target_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
|
cairo_surface_t *cairo_target_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
|
||||||
cairo_t *cairo = cairo_create(cairo_target_surface);
|
cairo_t *cairo = cairo_create(cairo_target_surface);
|
||||||
memset(cairo_image_surface_get_data(cairo_target_surface), 0, size);
|
memset(cairo_image_surface_get_data(cairo_target_surface), 0, size);
|
||||||
if (state->user_request.backdrop) {
|
|
||||||
cairo_set_source_rgba(cairo, 0x11 / 255.0, 0x11 / 255.0, 0x11 / 255.0, 0.85);
|
/* Draw background */
|
||||||
draw_rounded_rectangle(cairo, 0, 0, width, height, 15);
|
if (!color_argb_equal(&state->user_request.backdrop, &color_transparent_black)) {
|
||||||
/*cairo_rectangle(cairo, 0, 0, width, height);*/
|
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);
|
cairo_fill(cairo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Load and draw PNG */
|
/* Load and draw PNG */
|
||||||
if (strcmp("", state->user_request.graphics_filename) != STR_EQUAL) {
|
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);
|
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) {
|
if (cairo_surface_status(cairo_png_surface) != CAIRO_STATUS_SUCCESS) {
|
||||||
fprintf(stderr, "Failed to open PNG image.\n");
|
fprintf(stderr, "Failed to open PNG image.\n");
|
||||||
exit(1);
|
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.margin) / img_width;
|
||||||
|
double height_scale = (((double)height - 2*state->user_request.margin) * 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_set_source_surface(cairo, cairo_png_surface, 0.0, 0.0);
|
||||||
cairo_paint(cairo);
|
cairo_paint(cairo);
|
||||||
cairo_surface_destroy(cairo_png_surface);
|
cairo_surface_destroy(cairo_png_surface);
|
||||||
|
cairo_restore(cairo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Draw text */
|
/* Draw text */
|
||||||
if (state->user_request.text) {
|
if (state->user_request.text) {
|
||||||
PangoLayout *layout = pango_cairo_create_layout(cairo);
|
PangoLayout *layout = pango_cairo_create_layout(cairo);
|
||||||
pango_layout_set_text(layout, state->user_request.text, -1);
|
pango_layout_set_text(layout, state->user_request.text, -1);
|
||||||
PangoFontDescription *desc = pango_font_description_from_string("Fira Sans 12");
|
pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
|
||||||
|
pango_layout_set_width(layout, (width - 2*state->user_request.margin) * 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_layout_set_font_description(layout, desc);
|
||||||
pango_font_description_free(desc);
|
pango_font_description_free(desc);
|
||||||
cairo_set_source_rgb(cairo, 1.0, 1.0, 1.0);
|
set_source_argb_cairo(cairo, &state->user_request.text_color);
|
||||||
int text_width, text_height;
|
int text_width = 0, text_height = 0;
|
||||||
pango_layout_get_size(layout, &text_width, &text_height);
|
pango_layout_get_size(layout, &text_width, &text_height);
|
||||||
cairo_move_to(cairo, (width - text_width/PANGO_SCALE)/2.0, 3*height/4.0);
|
cairo_move_to(cairo, state->user_request.margin, 3*height/4.0);
|
||||||
pango_cairo_show_layout(cairo, layout);
|
pango_cairo_show_layout(cairo, layout);
|
||||||
g_object_unref(layout);
|
g_object_unref(layout);
|
||||||
}
|
}
|
||||||
|
@ -223,29 +286,118 @@ static const struct wl_registry_listener wl_registry_listener = {
|
||||||
.global_remove = registry_global_remove,
|
.global_remove = registry_global_remove,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
parse_hex(char *hex, struct color_argb *result_color) {
|
||||||
|
if (strlen(hex) != 4 * 2) {
|
||||||
|
// Invalid
|
||||||
|
result_color->a = -1.0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int a, r, g, b;
|
||||||
|
sscanf(hex, "%02x%02x%02x%02x", &a, &r, &g, &b);
|
||||||
|
result_color->a = a / 255.0;
|
||||||
|
result_color->r = r / 255.0;
|
||||||
|
result_color->g = g / 255.0;
|
||||||
|
result_color->b = b / 255.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
print_usage()
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: wl-overlay [options...] <image.png> [text]\n\n"
|
||||||
|
"image.png must be the path to a valid PNG image or may be the empty string for "
|
||||||
|
"no image. text is the text to show.\n\n"
|
||||||
|
"Options:\n"
|
||||||
|
" -h --help Show this help text and exit\n"
|
||||||
|
" --backdrop=<aarrggbb> Set backdrop colour (default d9111111, set to 00000000 to disable backdrop)\n"
|
||||||
|
" --color=<aarrggbb> Set text colour (default ffffffff)\n"
|
||||||
|
" --border-radius=<int> Give the backdrop rounded corners with specified radius (default %d)\n"
|
||||||
|
" --font=<str> Set font of text (default ‘" DEFAULT_FONT "’)\n"
|
||||||
|
" --width=<int> \n"
|
||||||
|
" --height=<int> Set width and height (default %d×%d)\n"
|
||||||
|
" --height=<int> Set margin around image and text (default %d)\n",
|
||||||
|
DEFAULT_BORDER_RADIUS,
|
||||||
|
DEFAULT_WIDTH, DEFAULT_HEIGHT,
|
||||||
|
DEFAULT_MARGIN);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char *argv[])
|
main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
struct client_state state = { 0 };
|
struct client_state state = { 0 };
|
||||||
|
state.user_request.text_color = (struct color_argb){1.0, 1.0, 1.0, 1.0};
|
||||||
|
state.user_request.border_radius = DEFAULT_BORDER_RADIUS;
|
||||||
|
state.user_request.backdrop = DEFAULT_BACKDROP_COLOR;
|
||||||
|
state.user_request.width = DEFAULT_WIDTH;
|
||||||
|
state.user_request.height = DEFAULT_HEIGHT;
|
||||||
|
state.user_request.margin = DEFAULT_MARGIN;
|
||||||
|
|
||||||
int i = 1;
|
int c;
|
||||||
if (argc < 2 || strcmp("-h", argv[i]) == STR_EQUAL || strcmp("--help", argv[i]) == STR_EQUAL) {
|
|
||||||
fprintf(stderr, "Usage: wl-overlay <image.png> [text]\n\nThe image must be a 256×256 PNG with ARGB.\n");
|
while (1) {
|
||||||
return 1;
|
int option_index = 0;
|
||||||
|
static struct option long_options[] = {
|
||||||
|
{"help", no_argument, 0, 'h'},
|
||||||
|
{"backdrop", required_argument, 0, 0 },
|
||||||
|
{"color", required_argument, 0, 0 },
|
||||||
|
{"colour", required_argument, 0, 0 },
|
||||||
|
{"border-radius", required_argument, 0, 0 },
|
||||||
|
{"font", required_argument, 0, 0 },
|
||||||
|
{"width", required_argument, 0, 0 },
|
||||||
|
{"height", required_argument, 0, 0 },
|
||||||
|
{"margin", required_argument, 0, 0 },
|
||||||
|
{0, 0, 0, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
c = getopt_long(argc, argv, "h", long_options, &option_index);
|
||||||
|
if (c == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case 0:
|
||||||
|
switch (option_index) {
|
||||||
|
case 1: parse_hex(optarg, &state.user_request.backdrop); break;
|
||||||
|
case 2: case 3: parse_hex(optarg, &state.user_request.text_color); break;
|
||||||
|
case 4: state.user_request.border_radius = atoi(optarg); break;
|
||||||
|
case 5: state.user_request.font = strdup(optarg); break;
|
||||||
|
case 6: state.user_request.width = atoi(optarg); break;
|
||||||
|
case 7: state.user_request.height = atoi(optarg); break;
|
||||||
|
case 8: state.user_request.margin = atoi(optarg); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
print_usage();
|
||||||
|
exit(0);
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (argc > i && strcmp("--backdrop", argv[i]) == STR_EQUAL) {
|
|
||||||
state.user_request.backdrop = true;
|
if (optind < argc) {
|
||||||
i++;
|
state.user_request.graphics_filename = strdup(argv[optind]);
|
||||||
} else {
|
} else {
|
||||||
state.user_request.backdrop = false;
|
print_usage();
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
state.user_request.graphics_filename = strdup(argv[i++]);
|
optind++;
|
||||||
if (argc > i) {
|
if (optind < argc) {
|
||||||
state.user_request.text = strdup(argv[i++]);
|
state.user_request.text = strdup(argv[optind]);
|
||||||
} else {
|
} else {
|
||||||
state.user_request.text = NULL;
|
state.user_request.text = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (color_argb_invalid(&state.user_request.backdrop)) {
|
||||||
|
fprintf(stderr, "Invalid --backdrop color, must be hexadecimal aarrggbb\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (color_argb_invalid(&state.user_request.text_color)) {
|
||||||
|
fprintf(stderr, "Invalid --font color, must be hexadecimal aarrggbb\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
state.wl_display = wl_display_connect(NULL);
|
state.wl_display = wl_display_connect(NULL);
|
||||||
if (!state.wl_display) {
|
if (!state.wl_display) {
|
||||||
fprintf(stderr, "Could not connect to Wayland server\n");
|
fprintf(stderr, "Could not connect to Wayland server\n");
|
||||||
|
@ -258,7 +410,7 @@ main(int argc, char *argv[])
|
||||||
state.wl_surface = wl_compositor_create_surface(state.wl_compositor);
|
state.wl_surface = wl_compositor_create_surface(state.wl_compositor);
|
||||||
state.zwlr_surface = zwlr_layer_shell_v1_get_layer_surface(
|
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");
|
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_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_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_set_margin(state.zwlr_surface, 50, 50, 50, 50);
|
||||||
|
|
||||||
|
@ -274,6 +426,9 @@ main(int argc, char *argv[])
|
||||||
if (state.user_request.text != NULL) {
|
if (state.user_request.text != NULL) {
|
||||||
free(state.user_request.text);
|
free(state.user_request.text);
|
||||||
}
|
}
|
||||||
|
if (state.user_request.font != NULL) {
|
||||||
|
free(state.user_request.font);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue