diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index a3b4b30c..14f68038 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -89,6 +89,20 @@ The rest of this man page describes configuration options. *outlines* [yes|no] Draw an outline around the selected window when switching between windows. Default is yes. +** + Define window switcher fields. + + *content* defines what the field shows and can be any of: + + - *type* Show view type ("xdg-shell" or "xwayland") + + - *app_id* Show app_id (class for XWayland) + + - *title* Show window title if different to app_id + + *width* defines the width of the field expressed as a percentage of + the overall window switcher width. The "%" character is required. + ## RESISTANCE ** diff --git a/docs/rc.xml.all b/docs/rc.xml.all index 9a04ede9..648bb5f3 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -38,7 +38,13 @@ - + + + + + + + diff --git a/include/config/rcxml.h b/include/config/rcxml.h index 18f79ea2..cdc01df8 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -11,6 +11,19 @@ #include "config/libinput.h" #include "theme.h" +enum window_switcher_field_content { + LAB_FIELD_NONE = 0, + LAB_FIELD_TYPE, + LAB_FIELD_APP_ID, + LAB_FIELD_TITLE, +}; + +struct window_switcher_field { + enum window_switcher_field_content content; + int width; + struct wl_list link; /* struct rcxml.window_switcher.fields */ +}; + struct rcxml { char *config_dir; @@ -65,6 +78,10 @@ struct rcxml { /* Regions */ struct wl_list regions; /* struct region.link */ + + struct { + struct wl_list fields; /* struct window_switcher_field.link */ + } window_switcher; }; extern struct rcxml rc; diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 82f7f1e2..56494328 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -31,6 +31,8 @@ static bool in_regions; static bool in_keybind; static bool in_mousebind; static bool in_libinput_category; +static bool in_window_switcher_field; + static struct keybind *current_keybind; static struct mousebind *current_mousebind; static struct libinput_category *current_libinput_category; @@ -38,6 +40,7 @@ static const char *current_mouse_context; static struct action *current_keybind_action; static struct action *current_mousebind_action; static struct region *current_region; +static struct window_switcher_field *current_field; enum font_place { FONT_PLACE_NONE = 0, @@ -51,6 +54,43 @@ enum font_place { static void load_default_key_bindings(void); static void load_default_mouse_bindings(void); +static void +fill_window_switcher_field(char *nodename, char *content) +{ + if (!strcasecmp(nodename, "field.fields.windowswitcher")) { + current_field = znew(*current_field); + wl_list_append(&rc.window_switcher.fields, ¤t_field->link); + return; + } + + string_truncate_at_pattern(nodename, ".field.fields.windowswitcher"); + if (!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, "app_id")) { + current_field->content = LAB_FIELD_APP_ID; + } else if (!strcmp(content, "title")) { + current_field->content = LAB_FIELD_TITLE; + } 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); + } +} + static void fill_region(char *nodename, char *content) { @@ -362,6 +402,10 @@ entry(xmlNode *node, char *nodename, char *content) fill_region(nodename, content); return; } + if (in_window_switcher_field) { + fill_window_switcher_field(nodename, content); + return; + } /* handle nodes without content, e.g. */ if (!strcmp(nodename, "default.keyboard")) { @@ -448,6 +492,14 @@ entry(xmlNode *node, char *nodename, char *content) } else if (strstr(nodename, "windowswitcher.core")) { wlr_log(WLR_ERROR, " should not be child of "); + /* The following three are for backward compatibility only */ + } else if (!strcasecmp(nodename, "show.windowSwitcher.core")) { + rc.cycle_view_osd = get_bool(content); + } else if (!strcasecmp(nodename, "preview.windowSwitcher.core")) { + rc.cycle_preview_contents = get_bool(content); + } else if (!strcasecmp(nodename, "outlines.windowSwitcher.core")) { + rc.cycle_preview_outlines = get_bool(content); + /* The following three are for backward compatibility only */ } else if (!strcasecmp(nodename, "cycleViewOSD.core")) { rc.cycle_view_osd = get_bool(content); @@ -531,6 +583,12 @@ xml_tree_walk(xmlNode *node) in_regions = false; continue; } + if (!strcasecmp((char *)n->name, "fields")) { + in_window_switcher_field = true; + traverse(n); + in_window_switcher_field = false; + continue; + } traverse(n); } } @@ -568,6 +626,7 @@ rcxml_init(void) wl_list_init(&rc.libinput_categories); wl_list_init(&rc.workspace_config.workspaces); wl_list_init(&rc.regions); + wl_list_init(&rc.window_switcher.fields); } has_run = true; @@ -784,6 +843,29 @@ deduplicate_key_bindings(void) } } +static struct { + enum window_switcher_field_content content; + int width; +} fields[] = { + { LAB_FIELD_TYPE, 25 }, + { LAB_FIELD_APP_ID, 25 }, + { LAB_FIELD_TITLE, 50 }, + { LAB_FIELD_NONE, 0 }, +}; + +static void +load_default_window_switcher_fields(void) +{ + struct window_switcher_field *field; + + for (int i = 0; fields[i].content != LAB_FIELD_NONE; i++) { + field = znew(*field); + field->content = fields[i].content; + field->width = fields[i].width; + wl_list_append(&rc.window_switcher.fields, &field->link); + } +} + static void post_processing(void) { @@ -846,6 +928,11 @@ post_processing(void) free(region); } } + + if (!wl_list_length(&rc.window_switcher.fields)) { + wlr_log(WLR_INFO, "load default window switcher fields"); + load_default_window_switcher_fields(); + } } static void @@ -953,6 +1040,12 @@ rcxml_finish(void) regions_destroy(NULL, &rc.regions); + 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); + } + /* Reset state vars for starting fresh when Reload is triggered */ current_keybind = NULL; current_mousebind = NULL; @@ -961,4 +1054,5 @@ rcxml_finish(void) current_keybind_action = NULL; current_mousebind_action = NULL; current_region = NULL; + current_field = NULL; } diff --git a/src/osd.c b/src/osd.c index 8c2ee64e..d2f5a567 100644 --- a/src/osd.c +++ b/src/osd.c @@ -21,8 +21,6 @@ #define OSD_ITEM_WIDTH (600) #define OSD_ITEM_PADDING (10) #define OSD_BORDER_WIDTH (6) -#define OSD_TAB1 (120) -#define OSD_TAB2 (300) /* is title different from app_id/class? */ static int @@ -243,11 +241,52 @@ 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_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(struct view *view) +{ + if (is_title_different(view)) { + return view_get_string_prop(view, "title"); + } else { + return ""; + } +} + static void -render_osd(cairo_t *cairo, int w, int h, struct wl_list *node_list, - struct view *cycle_view, struct theme *theme, bool show_workspace, +render_osd(struct server *server, cairo_t *cairo, int w, int h, + struct wl_list *node_list, bool show_workspace, const char *workspace_name) { + struct view *cycle_view = server->osd_state.cycle_view; + struct theme *theme = server->theme; + struct wlr_scene_node *node; cairo_surface_t *surf = cairo_get_target(cairo); @@ -270,11 +309,6 @@ render_osd(cairo_t *cairo, int w, int h, struct wl_list *node_list, PangoFontDescription *desc = font_to_pango_desc(&rc.font_osd); pango_layout_set_font_description(layout, desc); - PangoTabArray *tabs = pango_tab_array_new_with_positions(2, TRUE, - PANGO_TAB_LEFT, OSD_TAB1, PANGO_TAB_LEFT, OSD_TAB2); - pango_layout_set_tabs(layout, tabs); - pango_tab_array_free(tabs); - pango_cairo_update_layout(cairo, layout); int y = OSD_BORDER_WIDTH; @@ -313,31 +347,31 @@ render_osd(cairo_t *cairo, int w, int h, struct wl_list *node_list, if (!isfocusable(view)) { continue; } - buf.len = 0; - cairo_move_to(cairo, OSD_BORDER_WIDTH + OSD_ITEM_PADDING, y); - switch (view->type) { - case LAB_XDG_SHELL_VIEW: - buf_add(&buf, "[xdg-shell]\t"); - buf_add(&buf, get_formatted_app_id(view)); - buf_add(&buf, "\t"); - break; -#if HAVE_XWAYLAND - case LAB_XWAYLAND_VIEW: - buf_add(&buf, "[xwayland]\t"); - buf_add(&buf, view_get_string_prop(view, "class")); - buf_add(&buf, "\t"); - break; -#endif + int x = OSD_BORDER_WIDTH + OSD_ITEM_PADDING; + struct window_switcher_field *field; + wl_list_for_each(field, &rc.window_switcher.fields, link) { + buf.len = 0; + cairo_move_to(cairo, x, y); + + switch (field->content) { + case LAB_FIELD_TYPE: + buf_add(&buf, get_type(view)); + break; + case LAB_FIELD_APP_ID: + buf_add(&buf, get_app_id(view)); + break; + case LAB_FIELD_TITLE: + buf_add(&buf, get_title(view)); + break; + default: + break; + } + pango_layout_set_text(layout, buf.buf, -1); + pango_cairo_show_layout(cairo, layout); + x += field->width / 100.0 * OSD_ITEM_WIDTH; } - if (is_title_different(view)) { - buf_add(&buf, view_get_string_prop(view, "title")); - } - - pango_layout_set_text(layout, buf.buf, -1); - pango_cairo_show_layout(cairo, layout); - if (view == cycle_view) { /* Highlight current window */ cairo_rectangle(cairo, OSD_BORDER_WIDTH, y - y_offset, @@ -378,8 +412,7 @@ display_osd(struct output *output) /* Render OSD image */ cairo_t *cairo = output->osd_buffer->cairo; - render_osd(cairo, w, h, node_list, server->osd_state.cycle_view, - server->theme, show_workspace, workspace_name); + render_osd(server, cairo, w, h, node_list, show_workspace, workspace_name); struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_create( output->osd_tree, &output->osd_buffer->base);