From 4100922b7a96c3bdabd8880d772cb06a2ae8f07f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 9 Feb 2019 13:06:36 +0100 Subject: [PATCH] module/i3: allow 'content' to also contain a regular 'label' Previously, the 'content' of i3 was a map, where the keys where i3 workspace names, mapping to a particle template. Now, that configuration type is still allowed. But we also allow a variant where the 'content' is a list, of either 1 or 2 entries. Both entries *must* be dictionaries. The key is either 'dynlist', which is the same as the dictionary-only type of configuration. I.e. it maps to a dictionary where the keys are workspace names, mapping to particle templates. If the key is *not* dynlist, then it is assumed to be a particle name, and we treat that as a "regular" label, that will be appended after the workspace dynlist when instantiating the content. This particle will be instantiated with, for now, the title and application of the _currently focused_ workspace. --- modules/i3.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 120 insertions(+), 12 deletions(-) diff --git a/modules/i3.c b/modules/i3.c index 9a525a7..5d805e4 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -69,6 +69,8 @@ struct private { struct workspace *v; size_t count; } workspaces; + + struct particle *label; }; static bool @@ -736,6 +738,9 @@ destroy(struct module *mod) free(m->ws_content.v); workspaces_free(m); + if (m->label != NULL) + m->label->destroy(m->label); + free(m); module_default_destroy(mod); } @@ -759,7 +764,7 @@ content(struct module *mod) mtx_lock(&mod->lock); - struct exposable *particles[m->workspaces.count]; + struct exposable *particles[m->workspaces.count + (m->label != NULL)]; size_t particle_count = 0; for (size_t i = 0; i < m->workspaces.count; i++) { @@ -805,6 +810,32 @@ content(struct module *mod) tag_set_destroy(&tags); } + /* Find currently focused workspace */ + const struct workspace *ws = NULL; + size_t count = 0; + for (size_t i = 0; i < m->workspaces.count; i++) { + if (m->workspaces.v[i].focused) { + ws = &m->workspaces.v[i]; + count++; + } + } + + assert(count <= 1); + assert(count == 0 || ws != NULL); + + if (ws != NULL && m->label != NULL) { + struct tag_set tags = { + .tags = (struct tag *[]){ + tag_new_string(mod, "application", ws->window.application), + tag_new_string(mod, "title", ws->window.title), + }, + .count = 2, + }; + + particles[particle_count++] = m->label->instantiate(m->label, &tags); + tag_set_destroy(&tags); + } + mtx_unlock(&mod->lock); return dynlist_exposable_new( particles, particle_count, m->left_spacing, m->right_spacing); @@ -818,7 +849,7 @@ struct i3_workspaces { static struct module * i3_new(struct i3_workspaces workspaces[], size_t workspace_count, - int left_spacing, int right_spacing) + struct particle *label, int left_spacing, int right_spacing) { struct private *m = calloc(1, sizeof(*m)); @@ -833,6 +864,8 @@ i3_new(struct i3_workspaces workspaces[], size_t workspace_count, m->ws_content.v[i].content = workspaces[i].content; } + m->label = label; + struct module *mod = module_common_new(); mod->private = m; mod->run = &run; @@ -854,10 +887,37 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) int right = spacing != NULL ? yml_value_as_int(spacing) : right_spacing != NULL ? yml_value_as_int(right_spacing) : 0; - struct i3_workspaces workspaces[yml_dict_length(c)]; + const struct yml_node *ws_root_node = NULL; + struct particle *label = NULL; + + if (yml_is_dict(c)) { + ws_root_node = c; + } else { + for (struct yml_list_iter it = yml_list_iter(c); + it.node != NULL; + yml_list_next(&it)) + { + assert(yml_is_dict(it.node)); + assert(yml_dict_length(it.node) == 1); + + const struct yml_dict_iter item = yml_dict_iter(it.node); + const char *key = yml_value_as_string(item.key); + + if (strcmp(key, "dynlist") == 0) { + ws_root_node = item.value; + } else { + assert(label == NULL); + label = conf_to_particle(it.node, inherited); + } + } + } + + assert(yml_is_dict(ws_root_node)); + const size_t ws_count = yml_dict_length(ws_root_node); + struct i3_workspaces workspaces[ws_count]; size_t idx = 0; - for (struct yml_dict_iter it = yml_dict_iter(c); + for (struct yml_dict_iter it = yml_dict_iter(ws_root_node); it.key != NULL; yml_dict_next(&it), idx++) { @@ -865,18 +925,13 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) workspaces[idx].content = conf_to_particle(it.value, inherited); } - return i3_new(workspaces, yml_dict_length(c), left, right); + return i3_new(workspaces, ws_count, label, left, right); } static bool -verify_content(keychain_t *chain, const struct yml_node *node) +verify_content_dynlist(keychain_t *chain, const struct yml_node *node) { - if (!yml_is_dict(node)) { - LOG_ERR( - "%s: must be a dictionary of workspace-name: particle mappings", - conf_err_prefix(chain, node)); - return false; - } + assert(yml_is_dict(node)); for (struct yml_dict_iter it = yml_dict_iter(node); it.key != NULL; @@ -898,6 +953,59 @@ verify_content(keychain_t *chain, const struct yml_node *node) return true; } +static bool +verify_content(keychain_t *chain, const struct yml_node *node) +{ + if (!yml_is_dict(node) && !yml_is_list(node)) { + LOG_ERR( + "%s: must be a dictionary of workspace-name: particle mappings", + conf_err_prefix(chain, node)); + return false; + } + + if (yml_is_dict(node)) + return verify_content_dynlist(chain, node); + + if (yml_list_length(node) > 2) { + LOG_ERR("%s: must be a list of at most 2 entries", + conf_err_prefix(chain, node)); + return false; + } + + for (struct yml_list_iter it = yml_list_iter(node); + it.node != NULL; + yml_list_next(&it)) + { + if (!yml_is_dict(it.node) || yml_dict_length(it.node) != 1) { + LOG_ERR( + "%s: item must be a dictionary with a single item; " + "either a 'dynlist', or a particle name", + conf_err_prefix(chain, it.node)); + return false; + } + + const struct yml_dict_iter item = yml_dict_iter(it.node); + const char *key = yml_value_as_string(item.key); + if (key == NULL) { + LOG_ERR("%s: key must be a string; either 'dynlist', or a particle name", + conf_err_prefix(chain, item.key)); + return false; + } + + if (strcmp(key, "dynlist") == 0) { + if (!verify_content_dynlist(chain_push(chain, key), item.value)) + return false; + chain_pop(chain); + } else { + if (!conf_verify_particle(chain, it.node)) + return false; + } + + } + + return true; +} + static bool verify_conf(keychain_t *chain, const struct yml_node *node) {