From d453400ee6e19093af28a578d64cb7154dc52c59 Mon Sep 17 00:00:00 2001 From: Midgard Date: Wed, 7 Sep 2022 20:10:19 +0200 Subject: [PATCH] Add styling options --- makefile | 11 ++- wl-overlay.c | 199 +++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 181 insertions(+), 29 deletions(-) diff --git a/makefile b/makefile index 8e76db7..8930fab 100644 --- a/makefile +++ b/makefile @@ -2,19 +2,16 @@ BUILD = build CFLAGS += -Wall -# Pango and dependencies CFLAGS += $(shell pkg-config --cflags pangocairo) LDLIBS += $(shell pkg-config --libs pangocairo) -#CFLAGS += $(shell pkg-config --cflags glib-2.0) -#LDLIBS += $(shell pkg-config --libs glib-2.0) +CFLAGS += $(shell pkg-config --cflags cairo) +LDLIBS += $(shell pkg-config --libs cairo) -#CFLAGS += $(shell pkg-config --cflags harfbuzz) -#LDLIBS += $(shell pkg-config --libs harfbuzz) +CFLAGS += $(shell pkg-config --cflags wayland-client) +LDLIBS += $(shell pkg-config --libs wayland-client) -LDLIBS += -lwayland-client LDLIBS += -lrt -LDLIBS += -lcairo $(shell mkdir -p $(BUILD)) diff --git a/wl-overlay.c b/wl-overlay.c index bad1d53..f853264 100644 --- a/wl-overlay.c +++ b/wl-overlay.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -15,6 +16,14 @@ #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 */ static void randname(char *buf) @@ -63,10 +72,46 @@ allocate_shm_file(size_t size) } /* 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 { char *graphics_filename; 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 { @@ -128,40 +173,58 @@ draw_frame(struct client_state *state, const int width, const int height) wl_shm_pool_destroy(pool); close(fd); - /* Draw background */ 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); - if (state->user_request.backdrop) { - cairo_set_source_rgba(cairo, 0x11 / 255.0, 0x11 / 255.0, 0x11 / 255.0, 0.85); - draw_rounded_rectangle(cairo, 0, 0, width, height, 15); - /*cairo_rectangle(cairo, 0, 0, width, height);*/ + + /* 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.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_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); - 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_font_description_free(desc); - cairo_set_source_rgb(cairo, 1.0, 1.0, 1.0); - int text_width, text_height; + 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, (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); g_object_unref(layout); } @@ -223,29 +286,118 @@ static const struct wl_registry_listener wl_registry_listener = { .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...] [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= Set backdrop colour (default d9111111, set to 00000000 to disable backdrop)\n" + " --color= Set text colour (default ffffffff)\n" + " --border-radius= Give the backdrop rounded corners with specified radius (default %d)\n" + " --font= Set font of text (default ‘" DEFAULT_FONT "’)\n" + " --width= \n" + " --height= Set width and height (default %d×%d)\n" + " --height= Set margin around image and text (default %d)\n", + DEFAULT_BORDER_RADIUS, + DEFAULT_WIDTH, DEFAULT_HEIGHT, + DEFAULT_MARGIN); +} + int main(int argc, char *argv[]) { 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; - if (argc < 2 || strcmp("-h", argv[i]) == STR_EQUAL || strcmp("--help", argv[i]) == STR_EQUAL) { - fprintf(stderr, "Usage: wl-overlay [text]\n\nThe image must be a 256×256 PNG with ARGB.\n"); - return 1; + int c; + + while (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; - i++; + + if (optind < argc) { + state.user_request.graphics_filename = strdup(argv[optind]); } else { - state.user_request.backdrop = false; + print_usage(); + exit(1); } - state.user_request.graphics_filename = strdup(argv[i++]); - if (argc > i) { - state.user_request.text = strdup(argv[i++]); + optind++; + if (optind < argc) { + state.user_request.text = strdup(argv[optind]); } else { 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); if (!state.wl_display) { 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.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_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); @@ -274,6 +426,9 @@ main(int argc, char *argv[]) if (state.user_request.text != NULL) { free(state.user_request.text); } + if (state.user_request.font != NULL) { + free(state.user_request.font); + } return 0; }