diff --git a/modules/i3.c b/modules/i3.c index 5aa3d3b..05876c1 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -29,6 +29,7 @@ struct ws_content { }; struct workspace { + int id; char *name; int name_as_int; /* -1 is name is not a decimal number */ @@ -62,12 +63,29 @@ struct private { tll(struct workspace) workspaces; }; +static int +name_as_int(const char *name_as_string) +{ + int name_as_int = 0; + for (const char *p = name_as_string; *p != '\0'; p++) { + if (!(*p >= '0' && *p <= '9')) { + name_as_int = -1; + break; + } + + name_as_int *= 10; + name_as_int += *p - '0'; + } + return name_as_int; +} + static bool workspace_from_json(const struct json_object *json, struct workspace *ws) { /* Always present */ - struct json_object *name, *output; - if (!json_object_object_get_ex(json, "name", &name) || + struct json_object *id, *name, *output; + if (!json_object_object_get_ex(json, "id", &id) || + !json_object_object_get_ex(json, "name", &name) || !json_object_object_get_ex(json, "output", &output)) { LOG_ERR("workspace reply/event without 'name' and/or 'output' property"); @@ -82,20 +100,10 @@ workspace_from_json(const struct json_object *json, struct workspace *ws) const char *name_as_string = json_object_get_string(name); - int name_as_int = 0; - for (const char *p = name_as_string; *p != '\0'; p++) { - if (!(*p >= '0' && *p <= '9')) { - name_as_int = -1; - break; - } - - name_as_int *= 10; - name_as_int += *p - '0'; - } - *ws = (struct workspace) { + .id = json_object_get_int(id), .name = strdup(name_as_string), - .name_as_int = name_as_int, + .name_as_int = name_as_int(name_as_string), .output = strdup(json_object_get_string(output)), .visible = json_object_get_boolean(visible), .focused = json_object_get_boolean(focused), @@ -179,12 +187,12 @@ workspace_add(struct private *m, struct workspace ws) } static void -workspace_del(struct private *m, const char *name) +workspace_del(struct private *m, int id) { tll_foreach(m->workspaces, it) { struct workspace *ws = &it->item; - if (strcmp(ws->name, name) != 0) + if (ws->id != id) continue; workspace_free(ws); @@ -194,11 +202,11 @@ workspace_del(struct private *m, const char *name) } static struct workspace * -workspace_lookup(struct private *m, const char *name) +workspace_lookup(struct private *m, int id) { tll_foreach(m->workspaces, it) { struct workspace *ws = &it->item; - if (strcmp(ws->name, name) == 0) + if (ws->id == id) return ws; } return NULL; @@ -280,6 +288,7 @@ handle_workspace_event(int type, const struct json_object *json, void *_mod) bool is_init = strcmp(change_str, "init") == 0; bool is_empty = strcmp(change_str, "empty") == 0; bool is_focused = strcmp(change_str, "focus") == 0; + bool is_rename = strcmp(change_str, "rename") == 0; bool is_urgent = strcmp(change_str, "urgent") == 0; bool is_reload = strcmp(change_str, "reload") == 0; @@ -288,22 +297,22 @@ handle_workspace_event(int type, const struct json_object *json, void *_mod) return true; } - struct json_object *current, *_current_name; + struct json_object *current, *_current_id; if (!json_object_object_get_ex(json, "current", ¤t) || - !json_object_object_get_ex(current, "name", &_current_name)) + !json_object_object_get_ex(current, "id", &_current_id)) { - LOG_ERR("workspace event without 'current' and/or 'name' properties"); + LOG_ERR("workspace event without 'current' and/or 'id' properties"); return false; } - const char *current_name = json_object_get_string(_current_name); + int current_id = json_object_get_int(_current_id); mtx_lock(&mod->lock); if (is_init) { - struct workspace *already_exists = workspace_lookup(m, current_name); + struct workspace *already_exists = workspace_lookup(m, current_id); if (already_exists != NULL) { - LOG_WARN("workspace 'init' event for already existing workspace: %s", current_name); + LOG_WARN("workspace 'init' event for already existing workspace: %d", current_id); workspace_free(already_exists); if (!workspace_from_json(current, already_exists)) goto err; @@ -317,14 +326,14 @@ handle_workspace_event(int type, const struct json_object *json, void *_mod) } else if (is_empty) { - assert(workspace_lookup(m, current_name) != NULL); - workspace_del(m, current_name); + assert(workspace_lookup(m, current_id) != NULL); + workspace_del(m, current_id); } else if (is_focused) { - struct json_object *old, *_old_name, *urgent; + struct json_object *old, *_old_id, *urgent; if (!json_object_object_get_ex(json, "old", &old) || - !json_object_object_get_ex(old, "name", &_old_name) || + !json_object_object_get_ex(old, "id", &_old_id) || !json_object_object_get_ex(current, "urgent", &urgent)) { LOG_ERR("workspace 'focused' event without 'old', 'name' and/or 'urgent' property"); @@ -332,7 +341,7 @@ handle_workspace_event(int type, const struct json_object *json, void *_mod) return false; } - struct workspace *w = workspace_lookup(m, current_name); + struct workspace *w = workspace_lookup(m, current_id); assert(w != NULL); LOG_DBG("w: %s", w->name); @@ -349,12 +358,38 @@ handle_workspace_event(int type, const struct json_object *json, void *_mod) w->visible = true; /* Old workspace is no longer focused */ - const char *old_name = json_object_get_string(_old_name); - struct workspace *old_w = workspace_lookup(m, old_name); + int old_id = json_object_get_int(_old_id); + struct workspace *old_w = workspace_lookup(m, old_id); if (old_w != NULL) old_w->focused = false; } + else if (is_rename) { + struct workspace *w = workspace_lookup(m, current_id); + assert(w != NULL); + + struct json_object *_current_name; + if (!json_object_object_get_ex(current, "name", &_current_name)) { + LOG_ERR("workspace 'rename' event without 'name' property"); + mtx_unlock(&mod->lock); + return false; + } + + free(w->name); + w->name = strdup(json_object_get_string(_current_name)); + w->name_as_int = name_as_int(w->name); + + /* Re-add the workspace to ensure correct sorting */ + struct workspace ws = *w; + tll_foreach(m->workspaces, it) { + if (it->item.id == current_id) { + tll_remove(m->workspaces, it); + break; + } + } + workspace_add(m, ws); + } + else if (is_urgent) { struct json_object *urgent; if (!json_object_object_get_ex(current, "urgent", &urgent)) { @@ -363,7 +398,7 @@ handle_workspace_event(int type, const struct json_object *json, void *_mod) return false; } - struct workspace *w = workspace_lookup(m, current_name); + struct workspace *w = workspace_lookup(m, current_id); w->urgent = json_object_get_boolean(urgent); }