diff --git a/common/pango.c b/common/pango.c index abc18281..e04bf80f 100644 --- a/common/pango.c +++ b/common/pango.c @@ -50,7 +50,7 @@ size_t escape_markup_text(const char *src, char *dest) { return length; } -PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, +PangoLayout *get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc, const char *text, double scale, bool markup) { PangoLayout *layout = pango_cairo_create_layout(cairo); PangoAttrList *attrs; @@ -73,16 +73,14 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, } pango_attr_list_insert(attrs, pango_attr_scale_new(scale)); - PangoFontDescription *desc = pango_font_description_from_string(font); pango_layout_set_font_description(layout, desc); pango_layout_set_single_paragraph_mode(layout, 1); pango_layout_set_attributes(layout, attrs); pango_attr_list_unref(attrs); - pango_font_description_free(desc); return layout; } -void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, +void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height, int *baseline, double scale, bool markup, const char *fmt, ...) { va_list args; va_start(args, fmt); @@ -99,7 +97,7 @@ void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, vsnprintf(buf, length, fmt, args); va_end(args); - PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); + PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup); pango_cairo_update_layout(cairo, layout); pango_layout_get_pixel_size(layout, width, height); if (baseline) { @@ -109,10 +107,9 @@ void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, free(buf); } -void get_text_metrics(const char *font, int *height, int *baseline) { +void get_text_metrics(const PangoFontDescription *description, int *height, int *baseline) { cairo_t *cairo = cairo_create(NULL); PangoContext *pango = pango_cairo_create_context(cairo); - PangoFontDescription *description = pango_font_description_from_string(font); // When passing NULL as a language, pango uses the current locale. PangoFontMetrics *metrics = pango_context_get_metrics(pango, description, NULL); @@ -120,12 +117,11 @@ void get_text_metrics(const char *font, int *height, int *baseline) { *height = *baseline + pango_font_metrics_get_descent(metrics) / PANGO_SCALE; pango_font_metrics_unref(metrics); - pango_font_description_free(description); g_object_unref(pango); cairo_destroy(cairo); } -void render_text(cairo_t *cairo, const char *font, +void render_text(cairo_t *cairo, const PangoFontDescription *desc, double scale, bool markup, const char *fmt, ...) { va_list args; va_start(args, fmt); @@ -142,7 +138,7 @@ void render_text(cairo_t *cairo, const char *font, vsnprintf(buf, length, fmt, args); va_end(args); - PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); + PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup); cairo_font_options_t *fo = cairo_font_options_create(); cairo_get_font_options(cairo, fo); pango_cairo_context_set_font_options(pango_layout_get_context(layout), fo); diff --git a/include/pango.h b/include/pango.h index 93affc23..1db113c2 100644 --- a/include/pango.h +++ b/include/pango.h @@ -13,12 +13,12 @@ * escaped string to dest if provided. */ size_t escape_markup_text(const char *src, char *dest); -PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, +PangoLayout *get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc, const char *text, double scale, bool markup); -void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, +void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height, int *baseline, double scale, bool markup, const char *fmt, ...); -void get_text_metrics(const char *font, int *height, int *baseline); -void render_text(cairo_t *cairo, const char *font, +void get_text_metrics(const PangoFontDescription *desc, int *height, int *baseline); +void render_text(cairo_t *cairo, PangoFontDescription *desc, double scale, bool markup, const char *fmt, ...); #endif diff --git a/include/sway/config.h b/include/sway/config.h index 05678c33..2b4aa972 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -17,6 +17,7 @@ #include "sway/input/tablet.h" #include "sway/tree/root.h" #include "wlr-layer-shell-unstable-v1-protocol.h" +#include // TODO: Refactor this shit @@ -504,7 +505,8 @@ struct sway_config { char *floating_scroll_right_cmd; enum sway_container_layout default_orientation; enum sway_container_layout default_layout; - char *font; + char *font; // Used for IPC. + PangoFontDescription *font_description; // Used internally for rendering and validating. int font_height; int font_baseline; bool pango_markup; diff --git a/include/swaybar/config.h b/include/swaybar/config.h index 4cacd21a..361acd99 100644 --- a/include/swaybar/config.h +++ b/include/swaybar/config.h @@ -6,6 +6,7 @@ #include "../include/config.h" #include "list.h" #include "util.h" +#include struct box_colors { uint32_t border; @@ -28,7 +29,7 @@ struct swaybar_config { char *status_command; bool pango_markup; uint32_t position; // zwlr_layer_surface_v1_anchor - char *font; + PangoFontDescription *font_description; char *sep_symbol; char *mode; char *hidden_state; diff --git a/include/swaynag/types.h b/include/swaynag/types.h index 3c3b2754..18f218e0 100644 --- a/include/swaynag/types.h +++ b/include/swaynag/types.h @@ -4,7 +4,8 @@ struct swaynag_type { char *name; - char *font; + char *font; // Used for debugging. + PangoFontDescription *font_description; char *output; uint32_t anchors; int32_t layer; // enum zwlr_layer_shell_v1_layer or -1 if unset diff --git a/sway/commands/font.c b/sway/commands/font.c index cea720f5..74bb6b9f 100644 --- a/sway/commands/font.c +++ b/sway/commands/font.c @@ -4,6 +4,7 @@ #include "sway/config.h" #include "log.h" #include "stringop.h" +#include struct cmd_results *cmd_font(int argc, char **argv) { struct cmd_results *error = NULL; @@ -16,12 +17,34 @@ struct cmd_results *cmd_font(int argc, char **argv) { if (strncmp(font, "pango:", 6) == 0) { config->pango_markup = true; config->font = strdup(font + 6); + free(font); } else { config->pango_markup = false; - config->font = strdup(font); + config->font = font; } - free(font); + // Parse the font early so we can reject it if it's not valid for pango. + // Also avoids re-parsing each time we render text. + PangoFontDescription *font_description = pango_font_description_from_string(config->font); + + const char *family = pango_font_description_get_family(font_description); + if (family == NULL) { + pango_font_description_free(font_description); + return cmd_results_new(CMD_FAILURE, "Invalid font family."); + } + + const gint size = pango_font_description_get_size(font_description); + if (size == 0) { + pango_font_description_free(font_description); + return cmd_results_new(CMD_FAILURE, "Invalid font size."); + } + + if (config->font_description != NULL) { + pango_font_description_free(config->font_description); + } + + config->font_description = font_description; config_update_font_height(); + return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/config.c b/sway/config.c index 8220ece0..b41dd871 100644 --- a/sway/config.c +++ b/sway/config.c @@ -243,6 +243,7 @@ static void config_defaults(struct sway_config *config) { config->default_layout = L_NONE; config->default_orientation = L_NONE; if (!(config->font = strdup("monospace 10"))) goto cleanup; + config->font_description = pango_font_description_from_string(config->font); config->urgent_timeout = 500; config->focus_on_window_activation = FOWA_URGENT; config->popup_during_fullscreen = POPUP_SMART; @@ -1006,7 +1007,7 @@ int workspace_output_cmp_workspace(const void *a, const void *b) { void config_update_font_height(void) { int prev_max_height = config->font_height; - get_text_metrics(config->font, &config->font_height, &config->font_baseline); + get_text_metrics(config->font_description, &config->font_height, &config->font_baseline); if (config->font_height != prev_max_height) { arrange_root(); diff --git a/sway/tree/container.c b/sway/tree/container.c index 09766ce5..04ef965f 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -520,7 +520,7 @@ static void render_titlebar_text_texture(struct sway_output *output, to_cairo_subpixel_order(output->wlr_output->subpixel)); } cairo_set_font_options(c, fo); - get_text_size(c, config->font, &width, NULL, &baseline, scale, + get_text_size(c, config->font_description, &width, NULL, &baseline, scale, config->pango_markup, "%s", text); cairo_surface_destroy(dummy_surface); cairo_destroy(c); @@ -554,7 +554,7 @@ static void render_titlebar_text_texture(struct sway_output *output, class->text[2], class->text[3]); cairo_move_to(cairo, 0, config->font_baseline * scale - baseline); - render_text(cairo, config->font, scale, pango_markup, "%s", text); + render_text(cairo, config->font_description, scale, pango_markup, "%s", text); cairo_surface_flush(surface); unsigned char *data = cairo_image_surface_get_data(surface); diff --git a/swaybar/config.c b/swaybar/config.c index abedaec0..5e828773 100644 --- a/swaybar/config.c +++ b/swaybar/config.c @@ -26,7 +26,7 @@ struct swaybar_config *init_config(void) { config->status_command = NULL; config->pango_markup = false; config->position = parse_position("bottom"); - config->font = strdup("monospace 10"); + config->font_description = pango_font_description_from_string("monospace 10"); config->mode = strdup("dock"); config->hidden_state = strdup("hide"); config->sep_symbol = NULL; @@ -105,7 +105,7 @@ void free_tray_binding(struct tray_binding *binding) { void free_config(struct swaybar_config *config) { free(config->status_command); - free(config->font); + pango_font_description_free(config->font_description); free(config->mode); free(config->hidden_state); free(config->sep_symbol); diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 2cb235bf..9d81a9fb 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -147,8 +147,10 @@ static bool ipc_parse_config( json_object *font = json_object_object_get(bar_config, "font"); if (font) { - free(config->font); - config->font = parse_font(json_object_get_string(font)); + pango_font_description_free(config->font_description); + char *font_value = parse_font(json_object_get_string(font)); + config->font_description = pango_font_description_from_string(font_value); + free(font_value); } json_object *gaps = json_object_object_get(bar_config, "gaps"); @@ -485,8 +487,7 @@ static bool handle_barconfig_update(struct swaybar *bar, const char *payload, destroy_layer_surface(output); wl_list_remove(&output->link); wl_list_insert(&bar->unused_outputs, &output->link); - } else if (!oldcfg->font || !newcfg->font || - strcmp(oldcfg->font, newcfg->font) != 0) { + } else if (!pango_font_description_equal(oldcfg->font_description, newcfg->font_description)) { output->height = 0; // force update height } } diff --git a/swaybar/render.c b/swaybar/render.c index 7e2f97b7..a878805e 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -62,7 +62,7 @@ static uint32_t render_status_line_error(struct render_context *ctx, double *x) int margin = 3; double ws_vertical_padding = output->bar->config->status_padding; - char *font = output->bar->config->font; + PangoFontDescription *font = output->bar->config->font_description; int text_width, text_height; get_text_size(cairo, font, &text_width, &text_height, NULL, 1, false, "%s", error); @@ -97,7 +97,7 @@ static uint32_t render_status_line_text(struct render_context *ctx, double *x) { cairo_set_source_u32(cairo, fontcolor); int text_width, text_height; - get_text_size(cairo, config->font, &text_width, &text_height, NULL, + get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1, config->pango_markup, "%s", text); double ws_vertical_padding = config->status_padding; @@ -115,7 +115,7 @@ static uint32_t render_status_line_text(struct render_context *ctx, double *x) { double text_y = height / 2.0 - text_height / 2.0; cairo_move_to(cairo, *x, (int)floor(text_y)); choose_text_aa_mode(ctx, fontcolor); - render_text(cairo, config->font, 1, config->pango_markup, "%s", text); + render_text(cairo, config->font_description, 1, config->pango_markup, "%s", text); *x -= margin; return output->height; } @@ -190,7 +190,7 @@ static uint32_t render_status_block(struct render_context *ctx, struct swaybar_output *output = ctx->output; struct swaybar_config *config = output->bar->config; int text_width, text_height; - get_text_size(cairo, config->font, &text_width, &text_height, NULL, 1, + get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1, block->markup, "%s", text); int margin = 3; @@ -199,7 +199,7 @@ static uint32_t render_status_block(struct render_context *ctx, int width = text_width; if (block->min_width_str) { int w; - get_text_size(cairo, config->font, &w, NULL, NULL, 1, block->markup, + get_text_size(cairo, config->font_description, &w, NULL, NULL, 1, block->markup, "%s", block->min_width_str); block->min_width = w; } @@ -229,7 +229,7 @@ static uint32_t render_status_block(struct render_context *ctx, int sep_block_width = block->separator_block_width; if (!edge) { if (config->sep_symbol) { - get_text_size(cairo, config->font, &sep_width, &sep_height, NULL, + get_text_size(cairo, config->font_description, &sep_width, &sep_height, NULL, 1, false, "%s", config->sep_symbol); uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; uint32_t _ideal_surface_height = _ideal_height; @@ -307,7 +307,7 @@ static uint32_t render_status_block(struct render_context *ctx, color = block->urgent ? config->colors.urgent_workspace.text : color; cairo_set_source_u32(cairo, color); choose_text_aa_mode(ctx, color); - render_text(cairo, config->font, 1, block->markup, "%s", text); + render_text(cairo, config->font_description, 1, block->markup, "%s", text); x_pos += width; if (block->border_set || block->urgent) { @@ -331,7 +331,7 @@ static uint32_t render_status_block(struct render_context *ctx, double sep_y = height / 2.0 - sep_height / 2.0; cairo_move_to(cairo, offset, (int)floor(sep_y)); choose_text_aa_mode(ctx, color); - render_text(cairo, config->font, 1, false, + render_text(cairo, config->font_description, 1, false, "%s", config->sep_symbol); } else { cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); @@ -354,7 +354,7 @@ static void predict_status_block_pos(cairo_t *cairo, struct swaybar_config *config = output->bar->config; int text_width, text_height; - get_text_size(cairo, config->font, &text_width, &text_height, NULL, 1, + get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1, block->markup, "%s", block->full_text); int margin = 3; @@ -364,7 +364,7 @@ static void predict_status_block_pos(cairo_t *cairo, if (block->min_width_str) { int w; - get_text_size(cairo, config->font, &w, NULL, NULL, + get_text_size(cairo, config->font_description, &w, NULL, NULL, 1, block->markup, "%s", block->min_width_str); block->min_width = w; } @@ -391,7 +391,7 @@ static void predict_status_block_pos(cairo_t *cairo, int sep_block_width = block->separator_block_width; if (!edge) { if (config->sep_symbol) { - get_text_size(cairo, config->font, &sep_width, &sep_height, NULL, + get_text_size(cairo, config->font_description, &sep_width, &sep_height, NULL, 1, false, "%s", config->sep_symbol); uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; uint32_t _ideal_surface_height = _ideal_height; @@ -426,7 +426,7 @@ static uint32_t predict_workspace_button_length(cairo_t *cairo, struct swaybar_config *config = output->bar->config; int text_width, text_height; - get_text_size(cairo, config->font, &text_width, &text_height, NULL, 1, + get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1, config->pango_markup, "%s", ws->label); int ws_vertical_padding = WS_VERTICAL_PADDING; @@ -474,7 +474,7 @@ static uint32_t predict_binding_mode_indicator_length(cairo_t *cairo, } int text_width, text_height; - get_text_size(cairo, config->font, &text_width, &text_height, NULL, + get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1, output->bar->mode_pango_markup, "%s", mode); @@ -551,7 +551,7 @@ static uint32_t render_binding_mode_indicator(struct render_context *ctx, cairo_t *cairo = ctx->cairo; struct swaybar_config *config = output->bar->config; int text_width, text_height; - get_text_size(cairo, config->font, &text_width, &text_height, NULL, + get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1, output->bar->mode_pango_markup, "%s", mode); @@ -592,7 +592,7 @@ static uint32_t render_binding_mode_indicator(struct render_context *ctx, cairo_set_source_u32(cairo, config->colors.binding_mode.text); cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y)); choose_text_aa_mode(ctx, config->colors.binding_mode.text); - render_text(cairo, config->font, 1, output->bar->mode_pango_markup, + render_text(cairo, config->font_description, 1, output->bar->mode_pango_markup, "%s", mode); return output->height; } @@ -626,7 +626,7 @@ static uint32_t render_workspace_button(struct render_context *ctx, cairo_t *cairo = ctx->cairo; int text_width, text_height; - get_text_size(cairo, config->font, &text_width, &text_height, NULL, + get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1, config->pango_markup, "%s", ws->label); int ws_vertical_padding = WS_VERTICAL_PADDING; @@ -666,7 +666,7 @@ static uint32_t render_workspace_button(struct render_context *ctx, cairo_set_source_u32(cairo, box_colors.text); cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y)); choose_text_aa_mode(ctx, box_colors.text); - render_text(cairo, config->font, 1, config->pango_markup, + render_text(cairo, config->font_description, 1, config->pango_markup, "%s", ws->label); struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); @@ -699,7 +699,7 @@ static uint32_t render_to_cairo(struct render_context *ctx) { cairo_paint(cairo); int th; - get_text_size(cairo, config->font, NULL, &th, NULL, 1, false, ""); + get_text_size(cairo, config->font_description, NULL, &th, NULL, 1, false, ""); uint32_t max_height = (th + WS_VERTICAL_PADDING * 4); /* * Each render_* function takes the actual height of the bar, and returns diff --git a/swaynag/config.c b/swaynag/config.c index 9aeec3c2..a0bf3197 100644 --- a/swaynag/config.c +++ b/swaynag/config.c @@ -227,7 +227,9 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, case 'f': // Font if (type) { free(type->font); + pango_font_description_free(type->font_description); type->font = strdup(optarg); + type->font_description = pango_font_description_from_string(type->font); } break; case 'l': // Detailed Message diff --git a/swaynag/render.c b/swaynag/render.c index d72f42c2..21b03289 100644 --- a/swaynag/render.c +++ b/swaynag/render.c @@ -9,7 +9,7 @@ static uint32_t render_message(cairo_t *cairo, struct swaynag *swaynag) { int text_width, text_height; - get_text_size(cairo, swaynag->type->font, &text_width, &text_height, NULL, + get_text_size(cairo, swaynag->type->font_description, &text_width, &text_height, NULL, 1, true, "%s", swaynag->message); int padding = swaynag->type->message_padding; @@ -22,7 +22,7 @@ static uint32_t render_message(cairo_t *cairo, struct swaynag *swaynag) { cairo_set_source_u32(cairo, swaynag->type->text); cairo_move_to(cairo, padding, (int)(ideal_height - text_height) / 2); - render_text(cairo, swaynag->type->font, 1, false, + render_text(cairo, swaynag->type->font_description, 1, false, "%s", swaynag->message); return ideal_surface_height; @@ -31,7 +31,7 @@ static uint32_t render_message(cairo_t *cairo, struct swaynag *swaynag) { static void render_details_scroll_button(cairo_t *cairo, struct swaynag *swaynag, struct swaynag_button *button) { int text_width, text_height; - get_text_size(cairo, swaynag->type->font, &text_width, &text_height, NULL, + get_text_size(cairo, swaynag->type->font_description, &text_width, &text_height, NULL, 1, true, "%s", button->text); int border = swaynag->type->button_border_thickness; @@ -50,17 +50,17 @@ static void render_details_scroll_button(cairo_t *cairo, cairo_set_source_u32(cairo, swaynag->type->button_text); cairo_move_to(cairo, button->x + border + padding, button->y + border + (button->height - text_height) / 2); - render_text(cairo, swaynag->type->font, 1, true, + render_text(cairo, swaynag->type->font_description, 1, true, "%s", button->text); } static int get_detailed_scroll_button_width(cairo_t *cairo, struct swaynag *swaynag) { int up_width, down_width, temp_height; - get_text_size(cairo, swaynag->type->font, &up_width, &temp_height, NULL, + get_text_size(cairo, swaynag->type->font_description, &up_width, &temp_height, NULL, 1, true, "%s", swaynag->details.button_up.text); - get_text_size(cairo, swaynag->type->font, &down_width, &temp_height, NULL, + get_text_size(cairo, swaynag->type->font_description, &down_width, &temp_height, NULL, 1, true, "%s", swaynag->details.button_down.text); @@ -83,7 +83,7 @@ static uint32_t render_detailed(cairo_t *cairo, struct swaynag *swaynag, swaynag->details.y = y + decor; swaynag->details.width = width - decor * 2; - PangoLayout *layout = get_pango_layout(cairo, swaynag->type->font, + PangoLayout *layout = get_pango_layout(cairo, swaynag->type->font_description, swaynag->details.message, 1, false); pango_layout_set_width(layout, (swaynag->details.width - padding * 2) * PANGO_SCALE); @@ -172,7 +172,7 @@ static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag, struct swaynag_button *button = swaynag->buttons->items[button_index]; int text_width, text_height; - get_text_size(cairo, swaynag->type->font, &text_width, &text_height, NULL, + get_text_size(cairo, swaynag->type->font_description, &text_width, &text_height, NULL, 1, true, "%s", button->text); int border = swaynag->type->button_border_thickness; @@ -201,7 +201,7 @@ static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag, cairo_set_source_u32(cairo, swaynag->type->button_text); cairo_move_to(cairo, button->x + padding, button->y + padding); - render_text(cairo, swaynag->type->font, 1, true, + render_text(cairo, swaynag->type->font_description, 1, true, "%s", button->text); *x = button->x - border;