diff --git a/common/list.c b/common/list.c index 45efc16f2..310296d87 100644 --- a/common/list.c +++ b/common/list.c @@ -53,3 +53,13 @@ void list_cat(list_t *list, list_t *source) { void list_sort(list_t *list, int compare(const void *left, const void *right)) { qsort(list->items, list->length, sizeof(void *), compare); } + +int list_seq_find(list_t *list, int compare(const void *item, const void *data), const void *data) { + for (int i = 0; i < list->length; i++) { + void *item = list->items[i]; + if (compare(item, data) == 0) { + return i; + } + } + return -1; +} diff --git a/include/config.h b/include/config.h index c93f9cafc..82aa71bfb 100644 --- a/include/config.h +++ b/include/config.h @@ -102,6 +102,8 @@ char *do_var_replacement(char *str); void apply_output_config(struct output_config *oc, swayc_t *output); void free_output_config(struct output_config *oc); +int workspace_output_cmp_workspace(const void *a, const void *b); + /** * Global config singleton. */ diff --git a/include/list.h b/include/list.h index aff6800f0..90d0ad36d 100644 --- a/include/list.h +++ b/include/list.h @@ -15,5 +15,8 @@ void list_del(list_t *list, int index); void list_cat(list_t *list, list_t *source); // See qsort void list_sort(list_t *list, int compare(const void *left, const void *right)); +// Return index for first item in list that returns 0 for given compare +// function or -1 if none matches. +int list_seq_find(list_t *list, int compare(const void *item, const void *cmp_to), const void *cmp_to); #endif diff --git a/include/stringop.h b/include/stringop.h index febbbaba5..bb681bcdb 100644 --- a/include/stringop.h +++ b/include/stringop.h @@ -14,6 +14,9 @@ char *strip_whitespace(char *str); char *strip_comments(char *str); void strip_quotes(char *str); +// strcmp that also handles null pointers. +int lenient_strcmp(char *a, char *b); + // Simply split a string with delims, free with `free_flat_list` list_t *split_string(const char *str, const char *delims); void free_flat_list(list_t *list); diff --git a/sway/commands.c b/sway/commands.c index 173f0f531..7f24f5ab0 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -1318,9 +1318,15 @@ static struct cmd_results *cmd_workspace(int argc, char **argv) { return error; } struct workspace_output *wso = calloc(1, sizeof(struct workspace_output)); - sway_log(L_DEBUG, "Assigning workspace %s to output %s", argv[0], argv[2]); wso->workspace = strdup(argv[0]); wso->output = strdup(argv[2]); + int i = -1; + if ((i = list_seq_find(config->workspace_outputs, workspace_output_cmp_workspace, wso)) != -1) { + struct workspace_output *old = config->workspace_outputs->items[i]; + free(old); // workspaces can only be assigned to a single output + list_del(config->workspace_outputs, i); + } + sway_log(L_DEBUG, "Assigning workspace %s to output %s", argv[0], argv[2]); list_add(config->workspace_outputs, wso); if (!config->reading) { // TODO: Move workspace to output. (dont do so when reloading) diff --git a/sway/config.c b/sway/config.c index 725dedd2b..2fceb6e21 100644 --- a/sway/config.c +++ b/sway/config.c @@ -375,3 +375,11 @@ char *do_var_replacement(char *str) { } return str; } + +// the naming is intentional (albeit long): a workspace_output_cmp function +// would compare two structs in full, while this method only compares the +// workspace. +int workspace_output_cmp_workspace(const void *a, const void *b) { + const struct workspace_output *wsa = a, *wsb = b; + return lenient_strcmp(wsa->workspace, wsb->workspace); +} diff --git a/sway/stringop.c b/sway/stringop.c index 8d6cac2fd..fe5a97ca1 100644 --- a/sway/stringop.c +++ b/sway/stringop.c @@ -74,6 +74,19 @@ void strip_quotes(char *str) { *end = '\0'; } +// strcmp that also handles null pointers. +int lenient_strcmp(char *a, char *b) { + if (a == b) { + return 0; + } else if (!a) { + return -1; + } else if (!b) { + return 1; + } else { + return strcmp(a, b); + } +} + list_t *split_string(const char *str, const char *delims) { list_t *res = create_list(); char *copy = strdup(str);