From: droc12345 <80716141+droc12345@users.noreply.github.com> Date: Wed, 10 Apr 2024 22:39:31 +0000 (-0500) Subject: osd: add window-switcher custom field (#1670) X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=d672765ea7c684c7b61cc277e7fd310f723effbd;p=proto%2Flabwc.git osd: add window-switcher custom field (#1670) Add custom field with subset of printf style formatting to replace the original field formats. Example: Mono space font recommended. May need OSD width adjusted Co-authored-by: @Consolatis (based on work done by them) --- diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 79ec4893..e6960882 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -243,6 +243,30 @@ this is for compatibility with Openbox. - *output* Show output id, if more than one output detected + - *custom* A printf style config that can replace all the above + fields are: + - 'B' - shell type, values [xwayland|xdg-shell] + - 'b' - shell type (short form), values [X|W] + - 'S' - state of window, values [M|m|F] (3 spaces allocated) + (maximized, minimized, fullscreen) + - 's' - state of window (short form), values [M|m|F] (1 space) + - 'I' - wm-class/app-id + - 'i' - wm-class/app-id trimmed, remove "org." if available + - 'W' - workspace name + - 'w' - workspace name (if more than 1 ws configured) + - 'O' - output name + - 'o' - output name (show if more than 1 monitor active) + - 'T' - title of window + - 't' - title of window (if different than wm-class/app-id) + Recommend using with a mono space font, to keep alignment. + - *custom - subset of printf options allowed -- man 3 printf* + - random text may be inserted + - field length, example "%10" use 10 spaces, even if text uses less + - left jusify text, example "%-" + - right justify text, example "%" instead of "%-" + - example, %-10 would left justify and make room for 10 charaters + - Only one custom format allowed now. Future enhancements may allow more than one. + *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 1f56632c..5fdcd346 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -77,7 +77,22 @@ - + + + + custom format - (introduced in 0.7.2) + It allows one to replace all the above "fields" with one line, using a + printf style format. For field explanations, "man 5 labwc-config". + + The example below would print "foobar",then type of window (wayland, X), + then state of window (M/m/F), then output (shows if more than 1 active), + then workspace name, then identifier/app-id, then the window title. + Uses 100% of OSD window width. + + + + + --> diff --git a/include/config/rcxml.h b/include/config/rcxml.h index 0a4f10a7..ff8b8136 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -15,18 +15,6 @@ #include "resize_indicator.h" #include "theme.h" -enum window_switcher_field_content { - LAB_FIELD_NONE = 0, - LAB_FIELD_TYPE, - LAB_FIELD_IDENTIFIER, - LAB_FIELD_TRIMMED_IDENTIFIER, - LAB_FIELD_TITLE, - LAB_FIELD_WORKSPACE, - LAB_FIELD_WIN_STATE, - LAB_FIELD_TYPE_SHORT, - LAB_FIELD_OUTPUT, -}; - enum view_placement_policy { LAB_PLACE_CENTER = 0, LAB_PLACE_CURSOR, @@ -53,12 +41,6 @@ struct usable_area_override { struct wl_list link; /* struct rcxml.usable_area_overrides */ }; -struct window_switcher_field { - enum window_switcher_field_content content; - int width; - struct wl_list link; /* struct rcxml.window_switcher.fields */ -}; - struct rcxml { /* from command line */ char *config_dir; diff --git a/include/labwc.h b/include/labwc.h index b60d7647..482ce35f 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -502,15 +502,6 @@ void server_init(struct server *server); void server_start(struct server *server); void server_finish(struct server *server); -/* Updates onscreen display 'alt-tab' buffer */ -void osd_update(struct server *server); -/* Closes the OSD */ -void osd_finish(struct server *server); -/* Moves preview views back into their original stacking order and state */ -void osd_preview_restore(struct server *server); -/* Notify OSD about a destroying view */ -void osd_on_view_destroy(struct view *view); - /* * wlroots "input inhibitor" extension (required for swaylock) blocks * any client other than the requesting client from receiving events diff --git a/include/osd.h b/include/osd.h new file mode 100644 index 00000000..4fa0fa3b --- /dev/null +++ b/include/osd.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_OSD_H +#define LABWC_OSD_H + +#include +#include + +/* TODO: add field with keyboard layout? */ +enum window_switcher_field_content { + LAB_FIELD_NONE = 0, + LAB_FIELD_TYPE, + LAB_FIELD_TYPE_SHORT, + LAB_FIELD_IDENTIFIER, + LAB_FIELD_TRIMMED_IDENTIFIER, + LAB_FIELD_TITLE, + LAB_FIELD_TITLE_SHORT, + LAB_FIELD_WORKSPACE, + LAB_FIELD_WORKSPACE_SHORT, + LAB_FIELD_WIN_STATE, + LAB_FIELD_WIN_STATE_ALL, + LAB_FIELD_OUTPUT, + LAB_FIELD_OUTPUT_SHORT, + LAB_FIELD_CUSTOM, + + LAB_FIELD_COUNT +}; + +struct window_switcher_field { + enum window_switcher_field_content content; + int width; + char *format; + struct wl_list link; /* struct rcxml.window_switcher.fields */ +}; + +struct buf; +struct view; +struct server; + +/* Updates onscreen display 'alt-tab' buffer */ +void osd_update(struct server *server); + +/* Closes the OSD */ +void osd_finish(struct server *server); + +/* Moves preview views back into their original stacking order and state */ +void osd_preview_restore(struct server *server); + +/* Notify OSD about a destroying view */ +void osd_on_view_destroy(struct view *view); + +/* Used by osd.c internally to render window switcher fields */ +void osd_field_get_content(struct window_switcher_field *field, + struct buf *buf, struct view *view); + +/* Used by rcxml.c when parsing the config */ +struct window_switcher_field *osd_field_create(void); +void osd_field_arg_from_xml_node(struct window_switcher_field *field, + const char *nodename, const char *content); +bool osd_field_validate(struct window_switcher_field *field); +void osd_field_free(struct window_switcher_field *field); + +#endif // LABWC_OSD_H diff --git a/src/action.c b/src/action.c index 61e28f27..e268c3d3 100644 --- a/src/action.c +++ b/src/action.c @@ -16,6 +16,7 @@ #include "debug.h" #include "labwc.h" #include "menu/menu.h" +#include "osd.h" #include "output-virtual.h" #include "placement.h" #include "regions.h" diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 5e5ce68b..2b594a08 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -30,6 +30,7 @@ #include "config/tablet.h" #include "config/rcxml.h" #include "labwc.h" +#include "osd.h" #include "regions.h" #include "view.h" #include "window-rules.h" @@ -187,7 +188,7 @@ static void fill_window_switcher_field(char *nodename, char *content) { if (!strcasecmp(nodename, "field.fields.windowswitcher")) { - current_field = znew(*current_field); + current_field = osd_field_create(); wl_list_append(&rc.window_switcher.fields, ¤t_field->link); return; } @@ -197,39 +198,8 @@ fill_window_switcher_field(char *nodename, char *content) /* intentionally left empty */ } else if (!current_field) { wlr_log(WLR_ERROR, "no "); - } else if (!strcmp(nodename, "content")) { - if (!strcmp(content, "type")) { - current_field->content = LAB_FIELD_TYPE; - } else if (!strcmp(content, "identifier")) { - current_field->content = LAB_FIELD_IDENTIFIER; - } else if (!strcmp(content, "app_id")) { - wlr_log(WLR_ERROR, "window-switcher field 'app_id' is deprecated"); - current_field->content = LAB_FIELD_IDENTIFIER; - } else if (!strcmp(content, "trimmed_identifier")) { - current_field->content = LAB_FIELD_TRIMMED_IDENTIFIER; - } else if (!strcmp(content, "title")) { - current_field->content = LAB_FIELD_TITLE; - } else if (!strcmp(content, "workspace")) { - current_field->content = LAB_FIELD_WORKSPACE; - } else if (!strcmp(content, "state")) { - current_field->content = LAB_FIELD_WIN_STATE; - } else if (!strcmp(content, "type_short")) { - current_field->content = LAB_FIELD_TYPE_SHORT; - } else if (!strcmp(content, "output")) { - current_field->content = LAB_FIELD_OUTPUT; - } else { - wlr_log(WLR_ERROR, "bad windowSwitcher field '%s'", content); - } - } else if (!strcmp(nodename, "width") && !strchr(content, '%')) { - wlr_log(WLR_ERROR, "Removing invalid field, %s='%s' misses" - " trailing %%", nodename, content); - wl_list_remove(¤t_field->link); - zfree(current_field); - } else if (!strcmp(nodename, "width")) { - current_field->width = atoi(content); } else { - wlr_log(WLR_ERROR, "Unexpected data in field parser: %s=\"%s\"", - nodename, content); + osd_field_arg_from_xml_node(current_field, nodename, content); } } @@ -1516,6 +1486,16 @@ validate(void) } validate_actions(); + + /* OSD fields */ + struct window_switcher_field *field, *field_tmp; + wl_list_for_each_safe(field, field_tmp, &rc.window_switcher.fields, link) { + if (!osd_field_validate(field)) { + wlr_log(WLR_ERROR, "Deleting invalid window switcher field %p", field); + wl_list_remove(&field->link); + osd_field_free(field); + } + } } void @@ -1645,7 +1625,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); + osd_field_free(field); } struct window_rule *rule, *rule_tmp; diff --git a/src/desktop.c b/src/desktop.c index 4141e974..e3feaa37 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -7,6 +7,7 @@ #include "labwc.h" #include "layers.h" #include "node.h" +#include "osd.h" #include "ssd.h" #include "view.h" #include "window-rules.h" diff --git a/src/input/keyboard.c b/src/input/keyboard.c index 415edffd..d0db6cb1 100644 --- a/src/input/keyboard.c +++ b/src/input/keyboard.c @@ -11,6 +11,7 @@ #include "input/key-state.h" #include "labwc.h" #include "menu/menu.h" +#include "osd.h" #include "regions.h" #include "view.h" #include "workspaces.h" diff --git a/src/meson.build b/src/meson.build index f3ff1412..a7450d91 100644 --- a/src/meson.build +++ b/src/meson.build @@ -12,6 +12,7 @@ labwc_sources = files( 'main.c', 'node.c', 'osd.c', + 'osd_field.c', 'output.c', 'output-virtual.c', 'overlay.c', diff --git a/src/osd.c b/src/osd.c index b41b1bf1..caefe66d 100644 --- a/src/osd.c +++ b/src/osd.c @@ -14,39 +14,13 @@ #include "common/scene-helpers.h" #include "config/rcxml.h" #include "labwc.h" -#include "theme.h" #include "node.h" +#include "osd.h" +#include "theme.h" #include "view.h" #include "window-rules.h" #include "workspaces.h" -static const char * -get_formatted_app_id(struct view *view) -{ - char *s = (char *)view_get_string_prop(view, "app_id"); - if (!s) { - return NULL; - } - return s; -} - -static const char * -get_trimmed_app_id(char *s) -{ - if (!s) { - return NULL; - } - /* remove the first two nodes of 'org.' strings */ - if (!strncmp(s, "org.", 4)) { - char *p = s + 4; - p = strchr(p, '.'); - if (p) { - return ++p; - } - } - return s; -} - static void destroy_osd_nodes(struct output *output) { @@ -225,63 +199,6 @@ preview_cycled_view(struct view *view) wlr_scene_node_raise_to_top(osd_state->preview_node); } -static const char * -get_type(struct view *view) -{ - switch (view->type) { - case LAB_XDG_SHELL_VIEW: - return "[xdg-shell]"; -#if HAVE_XWAYLAND - case LAB_XWAYLAND_VIEW: - return "[xwayland]"; -#endif - } - return ""; -} - -static const char * -get_type_short(struct view *view) -{ - switch (view->type) { - case LAB_XDG_SHELL_VIEW: - return "[W]"; -#if HAVE_XWAYLAND - case LAB_XWAYLAND_VIEW: - return "[X]"; -#endif - } - return ""; -} - -static const char * -get_app_id(struct view *view) -{ - switch (view->type) { - case LAB_XDG_SHELL_VIEW: - return get_formatted_app_id(view); -#if HAVE_XWAYLAND - case LAB_XWAYLAND_VIEW: - return view_get_string_prop(view, "class"); -#endif - } - return ""; -} - -static const char * -get_title_if_different(struct view *view) -{ - /* - * XWayland clients return WM_CLASS for 'app_id' so we don't need a - * special case for that here. - */ - const char *identifier = view_get_string_prop(view, "app_id"); - const char *title = view_get_string_prop(view, "title"); - if (!identifier) { - return title; - } - return (!title || !strcmp(identifier, title)) ? NULL : title; -} - static void render_osd(struct server *server, cairo_t *cairo, int w, int h, bool show_workspace, const char *workspace_name, @@ -374,50 +291,8 @@ 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; - } + osd_field_get_content(field, &buf, *view); + int field_width = (available_width - (nr_fields + 1) * theme->osd_window_switcher_item_padding_x) * field->width / 100.0; diff --git a/src/osd_field.c b/src/osd_field.c new file mode 100644 index 00000000..a804a18b --- /dev/null +++ b/src/osd_field.c @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include "common/mem.h" +#include "config/rcxml.h" +#include "view.h" +#include "workspaces.h" +#include "labwc.h" +#include "osd.h" + +/* includes '%', terminating 's' and NULL byte, 8 is enough for %-9999s */ +#define LAB_FIELD_SINGLE_FMT_MAX_LEN 8 +static_assert(LAB_FIELD_SINGLE_FMT_MAX_LEN <= 255, "fmt_position is a unsigned char"); + +/* forward declares */ +typedef void field_conversion_type(struct buf *buf, struct view *view, const char *format); +struct field_converter { + const char fmt_char; + field_conversion_type *fn; +}; + +static const struct field_converter field_converter[]; + +/* Internal helpers */ + +static const char * +get_app_id_or_class(struct view *view, bool trim) +{ + /* + * XWayland clients return WM_CLASS for 'app_id' so we don't need a + * special case for that here. + */ + const char *identifier = view_get_string_prop(view, "app_id"); + + /* remove the first two nodes of 'org.' strings */ + if (trim && identifier && !strncmp(identifier, "org.", 4)) { + char *p = (char *)identifier + 4; + p = strchr(p, '.'); + if (p) { + return ++p; + } + } + return identifier; +} + +static const char * +get_type(struct view *view, bool short_form) +{ + switch (view->type) { + case LAB_XDG_SHELL_VIEW: + return short_form ? "[W]" : "[xdg-shell]"; +#if HAVE_XWAYLAND + case LAB_XWAYLAND_VIEW: + return short_form ? "[X]" : "[xwayland]"; +#endif + } + return "???"; +} + +static const char * +get_title(struct view *view) +{ + return view_get_string_prop(view, "title"); +} + +static const char * +get_title_if_different(struct view *view) +{ + const char *identifier = get_app_id_or_class(view, /*trim*/ false); + const char *title = get_title(view); + if (!identifier) { + return title; + } + return (!title || !strcmp(identifier, title)) ? NULL : title; +} + +/* Field handlers */ +static void +field_set_type(struct buf *buf, struct view *view, const char *format) +{ + /* custom type conversion-specifier: B (backend) */ + buf_add(buf, get_type(view, /*short_form*/ false)); +} + +static void +field_set_type_short(struct buf *buf, struct view *view, const char *format) +{ + /* custom type conversion-specifier: b (backend) */ + buf_add(buf, get_type(view, /*short_form*/ true)); +} + +static void +field_set_workspace(struct buf *buf, struct view *view, const char *format) +{ + /* custom type conversion-specifier: W */ + buf_add(buf, view->workspace->name); +} + +static void +field_set_workspace_short(struct buf *buf, struct view *view, const char *format) +{ + /* custom type conversion-specifier: w */ + if (wl_list_length(&rc.workspace_config.workspaces) > 1) { + buf_add(buf, view->workspace->name); + } +} + +static void +field_set_win_state(struct buf *buf, struct view *view, const char *format) +{ + /* custom type conversion-specifier: s */ + 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_win_state_all(struct buf *buf, struct view *view, const char *format) +{ + /* custom type conversion-specifier: S */ + buf_add(buf, view->minimized ? "m" : " "); + buf_add(buf, view->maximized ? "M" : " "); + buf_add(buf, view->fullscreen ? "F" : " "); + /* TODO: add always-on-top and omnipresent ? */ +} + +static void +field_set_output(struct buf *buf, struct view *view, const char *format) +{ + /* custom type conversion-specifier: O */ + if (output_is_usable(view->output)) { + buf_add(buf, view->output->wlr_output->name); + } +} + +static void +field_set_output_short(struct buf *buf, struct view *view, const char *format) +{ + /* custom type conversion-specifier: o */ + if (wl_list_length(&view->server->outputs) > 1 && + output_is_usable(view->output)) { + buf_add(buf, view->output->wlr_output->name); + } +} + +static void +field_set_identifier(struct buf *buf, struct view *view, const char *format) +{ + /* custom type conversion-specifier: I */ + buf_add(buf, get_app_id_or_class(view, /*trim*/ false)); +} + +static void +field_set_identifier_trimmed(struct buf *buf, struct view *view, const char *format) +{ + /* custom type conversion-specifier: i */ + buf_add(buf, get_app_id_or_class(view, /*trim*/ true)); +} + +static void +field_set_title(struct buf *buf, struct view *view, const char *format) +{ + /* custom type conversion-specifier: T */ + buf_add(buf, get_title(view)); +} + +static void +field_set_title_short(struct buf *buf, struct view *view, const char *format) +{ + /* custom type conversion-specifier: t */ + buf_add(buf, get_title_if_different(view)); +} + +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; + } + + char fmt[LAB_FIELD_SINGLE_FMT_MAX_LEN]; + unsigned char fmt_position = 0; + + struct buf field_result; + buf_init(&field_result); + + char converted_field[4096]; + + for (const char *p = format; *p; p++) { + if (!fmt_position) { + if (*p == '%') { + fmt[fmt_position++] = *p; + } else { + /* + * Just relay anything not part of a + * format string to the output buffer. + */ + buf_add_char(buf, *p); + } + continue; + } + + /* Allow string formatting */ + /* TODO: add . for manual truncating? */ + /* + * Remove the || *p == '#' section as not used/needed + * change (*p >= '0' && *p <= '9') to isdigit(*p) + * changes by droc12345 + */ + if (*p == '-' || isdigit(*p)) { + if (fmt_position >= LAB_FIELD_SINGLE_FMT_MAX_LEN - 2) { + /* Leave space for terminating 's' and NULL byte */ + wlr_log(WLR_ERROR, + "single format string length exceeded: '%s'", p); + } else { + fmt[fmt_position++] = *p; + } + continue; + } + + /* Handlers */ + for (unsigned char i = 0; i < LAB_FIELD_COUNT; i++) { + if (*p != field_converter[i].fmt_char) { + continue; + } + + /* Generate the actual content*/ + field_converter[i].fn(&field_result, view, /*format*/ NULL); + + /* Throw it at snprintf to allow formatting / padding */ + fmt[fmt_position++] = 's'; + fmt[fmt_position++] = '\0'; + snprintf(converted_field, sizeof(converted_field), + fmt, field_result.buf); + + /* And finally write it to the output buffer */ + buf_add(buf, converted_field); + goto reset_format; + } + + wlr_log(WLR_ERROR, + "invalid format character found for osd %s: '%c'", + format, *p); + +reset_format: + /* Reset format string and tmp field result buffer */ + buf_clear(&field_result); + fmt_position = 0; + } + free(field_result.buf); +} + +static const struct field_converter field_converter[LAB_FIELD_COUNT] = { + [LAB_FIELD_TYPE] = { 'B', field_set_type }, + [LAB_FIELD_TYPE_SHORT] = { 'b', field_set_type_short }, + [LAB_FIELD_WIN_STATE_ALL] = { 'S', field_set_win_state_all }, + [LAB_FIELD_WIN_STATE] = { 's', field_set_win_state }, + [LAB_FIELD_IDENTIFIER] = { 'I', field_set_identifier }, + [LAB_FIELD_TRIMMED_IDENTIFIER] = { 'i', field_set_identifier_trimmed }, + [LAB_FIELD_WORKSPACE] = { 'W', field_set_workspace }, + [LAB_FIELD_WORKSPACE_SHORT] = { 'w', field_set_workspace_short }, + [LAB_FIELD_OUTPUT] = { 'O', field_set_output }, + [LAB_FIELD_OUTPUT_SHORT] = { 'o', field_set_output_short }, + [LAB_FIELD_TITLE] = { 'T', field_set_title }, + [LAB_FIELD_TITLE_SHORT] = { 't', field_set_title_short }, + /* fmt_char can never be matched so prevents LAB_FIELD_CUSTOM recursion */ + [LAB_FIELD_CUSTOM] = { '\0', field_set_custom }, +}; + +struct window_switcher_field * +osd_field_create(void) +{ + struct window_switcher_field *field = znew(*field); + return field; +} + +void +osd_field_arg_from_xml_node(struct window_switcher_field *field, + const char *nodename, const char *content) +{ + if (!strcmp(nodename, "content")) { + if (!strcmp(content, "type")) { + field->content = LAB_FIELD_TYPE; + } else if (!strcmp(content, "type_short")) { + field->content = LAB_FIELD_TYPE_SHORT; + } else if (!strcmp(content, "app_id")) { + wlr_log(WLR_ERROR, "window-switcher field 'app_id' is deprecated"); + field->content = LAB_FIELD_IDENTIFIER; + } else if (!strcmp(content, "identifier")) { + field->content = LAB_FIELD_IDENTIFIER; + } else if (!strcmp(content, "trimmed_identifier")) { + field->content = LAB_FIELD_TRIMMED_IDENTIFIER; + } else if (!strcmp(content, "title")) { + /* Keep old defaults */ + field->content = LAB_FIELD_TITLE_SHORT; + } else if (!strcmp(content, "workspace")) { + field->content = LAB_FIELD_WORKSPACE; + } else if (!strcmp(content, "state")) { + field->content = LAB_FIELD_WIN_STATE; + } else if (!strcmp(content, "output")) { + /* Keep old defaults */ + field->content = LAB_FIELD_OUTPUT_SHORT; + } else if (!strcmp(content, "custom")) { + field->content = LAB_FIELD_CUSTOM; + } else { + wlr_log(WLR_ERROR, "bad windowSwitcher field '%s'", content); + } + } else if (!strcmp(nodename, "format")) { + zfree(field->format); + field->format = xstrdup(content); + } else if (!strcmp(nodename, "width") && !strchr(content, '%')) { + wlr_log(WLR_ERROR, "Invalid osd field width: %s, misses trailing %%", content); + } else if (!strcmp(nodename, "width")) { + field->width = atoi(content); + } else { + wlr_log(WLR_ERROR, "Unexpected data in field parser: %s=\"%s\"", + nodename, content); + } +} + +bool +osd_field_validate(struct window_switcher_field *field) +{ + if (field->content == LAB_FIELD_NONE) { + wlr_log(WLR_ERROR, "Invalid OSD field: no content set"); + return false; + } + if (field->content == LAB_FIELD_CUSTOM && !field->format) { + wlr_log(WLR_ERROR, "Invalid OSD field: custom without format"); + return false; + } + if (!field->width) { + wlr_log(WLR_ERROR, "Invalid OSD field: no width"); + return false; + } + return true; +} + +void +osd_field_get_content(struct window_switcher_field *field, + struct buf *buf, struct view *view) +{ + if (field->content == LAB_FIELD_NONE) { + wlr_log(WLR_ERROR, "Invalid window switcher field type"); + return; + } + assert(field->content < LAB_FIELD_COUNT && field_converter[field->content].fn); + + field_converter[field->content].fn(buf, view, field->format); +} + +void +osd_field_free(struct window_switcher_field *field) +{ + zfree(field->format); + zfree(field); +} diff --git a/src/view.c b/src/view.c index 98ecf4de..9e8ab703 100644 --- a/src/view.c +++ b/src/view.c @@ -10,6 +10,7 @@ #include "input/keyboard.h" #include "labwc.h" #include "menu/menu.h" +#include "osd.h" #include "placement.h" #include "regions.h" #include "resize_indicator.h"