From 6311b3ac6a153094633721c743b99d8a909d3b39 Mon Sep 17 00:00:00 2001 From: Droc Date: Sun, 24 Mar 2024 08:40:47 -0500 Subject: [PATCH] Window switch, add custom field, with printf style config based on work by Consolatis. I added documentation, examples and repackaged. --- docs/labwc-config.5.scd | 3 + docs/rc.xml.all | 24 ++++- include/config/rcxml.h | 4 + src/config/rcxml.c | 5 + src/osd.c | 211 +++++++++++++++++++++++++++++++--------- 5 files changed, 202 insertions(+), 45 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 614821a6..a92c7882 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -224,6 +224,9 @@ this is for compatibility with Openbox. - *output* Show output id, if more than one output detected + - *custom* This field will replace any/all of the above. + It uses a printf style for it's formatting. + *width* defines the width of the field expressed as a percentage of the overall window switcher width. The "%" character is required. diff --git a/docs/rc.xml.all b/docs/rc.xml.all index 4de36af1..9204d432 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -77,8 +77,30 @@ - + + + A new custom field has been added that will effectively replace the above. + It's used like this. + + + + + + + + Note: you can embed text into the string, or rearrange the order of elements + or leave them out completely. Recommend using a monospace font for the osd + Printf style formatting, including spacing and justification. + Fields are: + %B -> type (backend) + %b -> type_short (backend) + %I -> identifier + %i -> trimmed_identifier + %s -> state + %o -> output + %W -> workspace + %t -> title --> diff --git a/include/config/rcxml.h b/include/config/rcxml.h index ac4d1795..1209542c 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -25,6 +25,9 @@ enum window_switcher_field_content { LAB_FIELD_WIN_STATE, LAB_FIELD_TYPE_SHORT, LAB_FIELD_OUTPUT, + LAB_FIELD_CUSTOM, + + LAB_FIELD_COUNT }; enum view_placement_policy { @@ -56,6 +59,7 @@ struct usable_area_override { struct window_switcher_field { enum window_switcher_field_content content; int width; + char *format; struct wl_list link; /* struct rcxml.window_switcher.fields */ }; diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 994498c3..e797c0aa 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -216,9 +216,13 @@ fill_window_switcher_field(char *nodename, char *content) current_field->content = LAB_FIELD_TYPE_SHORT; } else if (!strcmp(content, "output")) { current_field->content = LAB_FIELD_OUTPUT; + } else if (!strcmp(content, "custom")) { + current_field->content = LAB_FIELD_CUSTOM; } else { wlr_log(WLR_ERROR, "bad windowSwitcher field '%s'", content); } + } else if (!strcmp(nodename, "format")) { + current_field->format = xstrdup(content); } else if (!strcmp(nodename, "width") && !strchr(content, '%')) { wlr_log(WLR_ERROR, "Removing invalid field, %s='%s' misses" " trailing %%", nodename, content); @@ -1635,6 +1639,7 @@ rcxml_finish(void) struct window_switcher_field *field, *field_tmp; wl_list_for_each_safe(field, field_tmp, &rc.window_switcher.fields, link) { wl_list_remove(&field->link); + zfree(field->format); zfree(field); } diff --git a/src/osd.c b/src/osd.c index d6d6be56..9a89d225 100644 --- a/src/osd.c +++ b/src/osd.c @@ -11,6 +11,7 @@ #include "common/buf.h" #include "common/font.h" #include "common/graphic-helpers.h" +#include "common/mem.h" #include "common/scene-helpers.h" #include "config/rcxml.h" #include "labwc.h" @@ -282,6 +283,169 @@ get_title_if_different(struct view *view) return (!title || !strcmp(identifier, title)) ? NULL : title; } +typedef void field_set_fn_t(struct buf *buf, struct view *view, const char *format); + +static void +field_set_type(struct buf *buf, struct view *view, const char *format) +{ + buf_add(buf, get_type(view)); +} + +static void +field_set_type_short(struct buf *buf, struct view *view, const char *format) +{ + buf_add(buf, get_type_short(view)); +} + +static void +field_set_workspace(struct buf *buf, struct view *view, const char *format) +{ + buf_add(buf, view->workspace->name); +} + +static void +field_set_win_state(struct buf *buf, struct view *view, const char *format) +{ + if (view->maximized) { + buf_add(buf, "M"); + } else if (view->minimized) { + buf_add(buf, "m"); + } else if (view->fullscreen) { + buf_add(buf, "F"); + } else { + buf_add(buf, " "); + } +} + +static void +field_set_output(struct buf *buf, struct view *view, const char *format) +{ + if (wl_list_length(&view->server->outputs) > 1 && + output_is_usable(view->output)) { + buf_add(buf, view->output->wlr_output->name); + } else { + buf_add(buf, " "); + } +} + +static void +field_set_identifier(struct buf *buf, struct view *view, const char *format) +{ + buf_add(buf, get_app_id(view)); +} + +static void +field_set_identifier_trimmed(struct buf *buf, struct view *view, const char *format) +{ + char *s = (char *)get_app_id(view); + buf_add(buf, get_trimmed_app_id(s)); +} + +static void +field_set_title(struct buf *buf, struct view *view, const char *format) +{ + buf_add(buf, get_title_if_different(view)); +} + +/* forward declare */ +static field_set_fn_t * const field_handlers[]; + +static void +field_set_custom(struct buf *buf, struct view *view, const char *format) +{ + if (!format) { + wlr_log(WLR_ERROR, "Missing format for custom window switcher field"); + return; + } + + /* FIXME: completely random, add check in the string formatting branch */ + char fmt[20]; + unsigned char fmt_len = 0; + + struct buf field_result; + buf_init(&field_result); + + /* FIXME: similarly random */ + char field_formatted[4096]; + + for (const char *p = format; *p; p++) { + if (!fmt_len) { + if (*p == '%') { + fmt[fmt_len++] = *p; + } else { + /* + * Just relay anything not part of a + * format string to the output buffer. + */ + buf_add_char(buf, *p); + } + continue; + } + enum window_switcher_field_content field = LAB_FIELD_NONE; + + /* Allow string formatting */ + /* TODO: add . for manual truncating? */ + if (*p == '-' || *p == '#' || (*p >= '0' && *p <= '9')) { + fmt[fmt_len++] = *p; + continue; + } + + /* Handlers */ + if (*p == 'B') { + field = LAB_FIELD_TYPE; + } else if (*p == 'b') { + field = LAB_FIELD_TYPE_SHORT; + } else if (*p == 'W') { + field = LAB_FIELD_WORKSPACE; + } else if (*p == 's') { + field = LAB_FIELD_WIN_STATE; + } else if (*p == 'o') { + field = LAB_FIELD_OUTPUT; + } else if (*p == 'I') { + field = LAB_FIELD_IDENTIFIER; + } else if (*p == 'i') { + field = LAB_FIELD_TRIMMED_IDENTIFIER; + } else if (*p == 't') { + field = LAB_FIELD_TITLE; + } else { + wlr_log(WLR_ERROR, + "invalid format character found for osd %s: '%c'", + format, *p); + goto reset_format; + } + fmt[fmt_len++] = 's'; + fmt[fmt_len++] = '\0'; + + /* Generate the actual content*/ + assert(field < LAB_FIELD_COUNT && field_handlers[field]); + field_handlers[field](&field_result, view, NULL); + + /* Throw it at snprintf to allow formatting / padding */ + snprintf(field_formatted, sizeof(field_formatted), fmt, field_result.buf); + + /* And finally write it to the output buffer */ + buf_add(buf, field_formatted); + +reset_format: + /* Reset format string and tmp field result buffer */ + fmt_len = 0; + field_result.len = 0; + } + free(field_result.buf); +} + +static field_set_fn_t * const field_handlers[] = { + [LAB_FIELD_TYPE] = field_set_type, + [LAB_FIELD_TYPE_SHORT] = field_set_type_short, + [LAB_FIELD_WORKSPACE] = field_set_workspace, + [LAB_FIELD_WIN_STATE] = field_set_win_state, + [LAB_FIELD_OUTPUT] = field_set_output, + [LAB_FIELD_IDENTIFIER] = field_set_identifier, + [LAB_FIELD_TRIMMED_IDENTIFIER] = field_set_identifier_trimmed, + [LAB_FIELD_TITLE] = field_set_title, + [LAB_FIELD_CUSTOM] = field_set_custom, +}; + static void render_osd(struct server *server, cairo_t *cairo, int w, int h, bool show_workspace, const char *workspace_name, @@ -374,50 +538,9 @@ render_osd(struct server *server, cairo_t *cairo, int w, int h, + theme->osd_window_switcher_item_padding_y + theme->osd_window_switcher_item_active_border_width); - switch (field->content) { - case LAB_FIELD_TYPE: - buf_add(&buf, get_type(*view)); - break; - case LAB_FIELD_TYPE_SHORT: - buf_add(&buf, get_type_short(*view)); - break; - case LAB_FIELD_WORKSPACE: - buf_add(&buf, (*view)->workspace->name); - break; - case LAB_FIELD_WIN_STATE: - if ((*view)->maximized) { - buf_add(&buf, "M"); - } else if ((*view)->minimized) { - buf_add(&buf, "m"); - } else if ((*view)->fullscreen) { - buf_add(&buf, "F"); - } else { - buf_add(&buf, " "); - } - break; - case LAB_FIELD_OUTPUT: - if (wl_list_length(&server->outputs) > 1 && - output_is_usable((*view)->output)) { - buf_add(&buf, (*view)->output->wlr_output->name); - } else { - buf_add(&buf, " "); - } - break; - case LAB_FIELD_IDENTIFIER: - buf_add(&buf, get_app_id(*view)); - break; - case LAB_FIELD_TRIMMED_IDENTIFIER: - { - char *s = (char *)get_app_id(*view); - buf_add(&buf, get_trimmed_app_id(s)); - break; - } - case LAB_FIELD_TITLE: - buf_add(&buf, get_title_if_different(*view)); - break; - default: - break; - } + assert(field->content < LAB_FIELD_COUNT && field_handlers[field->content]); + field_handlers[field->content](&buf, *view, field->format); + int field_width = (available_width - (nr_fields + 1) * theme->osd_window_switcher_item_padding_x) * field->width / 100.0;