From ced800910d99de8f956a6f2d5ad84e5432a19bdc Mon Sep 17 00:00:00 2001 From: Emilia Miki Date: Wed, 24 Jun 2026 03:41:13 +0300 Subject: [PATCH 1/2] feat: add tag names this feature adds optional tag names in mango config, specified using the name key. if name is not specified, the default arabic numerals are used. --- src/config/parse_config.h | 16 ++++++++++++++++ src/ext-protocol/ext-workspace.h | 6 +++--- src/ipc/ipc.h | 1 + 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 61af421b..1f3e2d97 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -160,6 +160,7 @@ typedef struct { typedef struct { int32_t id; + char *name; char *layout_name; char *monitor_name; char *monitor_make; @@ -2183,6 +2184,7 @@ bool parse_option(Config *config, char *key, char *value) { // 设置默认值 rule->id = 0; + rule->name = NULL; rule->layout_name = NULL; rule->monitor_name = NULL; rule->monitor_make = NULL; @@ -2211,6 +2213,8 @@ bool parse_option(Config *config, char *key, char *value) { if (strcmp(key, "id") == 0) { rule->id = CLAMP_INT(atoi(val), 0, LENGTH(tags)); + } else if (strcmp(key, "name") == 0) { + rule->name = strdup(val); } else if (strcmp(key, "layout_name") == 0) { rule->layout_name = strdup(val); } else if (strcmp(key, "monitor_name") == 0) { @@ -3300,6 +3304,8 @@ void free_config(void) { // 释放 tag_rules if (config.tag_rules) { for (int32_t i = 0; i < config.tag_rules_count; i++) { + if (config.tag_rules[i].name) + free((void *)config.tag_rules[i].name); if (config.tag_rules[i].layout_name) free((void *)config.tag_rules[i].layout_name); if (config.tag_rules[i].monitor_name) @@ -4302,3 +4308,13 @@ int32_t reload_config(const Arg *arg) { printstatus(IPC_WATCH_ARRANGGE); return 1; } + +static const char *get_tag_name(int tag_index) { + for (int i = 0; i < config.tag_rules_count; i++) { + if (config.tag_rules[i].id == tag_index && config.tag_rules[i].name) + return config.tag_rules[i].name; + } + if (tag_index >= 1 && tag_index <= (int)LENGTH(tags)) + return tags[tag_index - 1]; + return NULL; +} diff --git a/src/ext-protocol/ext-workspace.h b/src/ext-protocol/ext-workspace.h index eea0f455..0c53cb11 100644 --- a/src/ext-protocol/ext-workspace.h +++ b/src/ext-protocol/ext-workspace.h @@ -100,9 +100,9 @@ static void handle_ext_commit(struct wl_listener *listener, void *data) { } static const char *get_name_from_tag(uint32_t tag) { - static const char *names[] = {"overview", "1", "2", "3", "4", - "5", "6", "7", "8", "9"}; - return (tag < sizeof(names) / sizeof(names[0])) ? names[tag] : NULL; + if (tag == 0) + return "overview"; + return get_tag_name((int)tag); } void destroy_workspace(struct workspace *workspace) { diff --git a/src/ipc/ipc.h b/src/ipc/ipc.h index 03e0746e..e66b2842 100644 --- a/src/ipc/ipc.h +++ b/src/ipc/ipc.h @@ -99,6 +99,7 @@ static cJSON *build_tags_json(Monitor *m) { } cJSON *tag_obj = cJSON_CreateObject(); cJSON_AddNumberToObject(tag_obj, "index", tag); + cJSON_AddStringToObject(tag_obj, "name", get_tag_name(tag)); cJSON_AddBoolToObject(tag_obj, "is_active", is_active); cJSON_AddBoolToObject(tag_obj, "is_urgent", is_urgent); cJSON_AddStringToObject(tag_obj, "layout", From c72a083bf6b3c39261c99b27ec1dba1f48345ac8 Mon Sep 17 00:00:00 2001 From: Emilia Miki Date: Tue, 30 Jun 2026 02:12:44 +0300 Subject: [PATCH 2/2] Add mmsg rename tag IPC message * Add mmsg rename tag IPC message for renaming tags --- docs/ipc.md | 11 ++++++++++ mmsg/mmsg.1 | 18 ++++++++++++++++ src/ext-protocol/ext-workspace.h | 2 ++ src/ipc/ipc.h | 37 +++++++++++++++++++++++++++++++- 4 files changed, 67 insertions(+), 1 deletion(-) diff --git a/docs/ipc.md b/docs/ipc.md index 0bdcc0b1..32b81b02 100644 --- a/docs/ipc.md +++ b/docs/ipc.md @@ -67,6 +67,17 @@ mmsg watch all-monitors mmsg watch all-tags ``` +### RENAME +| Command | Description | +| :--- | :--- | +| `rename tag ` | Renames a tag at runtime. Updates the in-memory config, the ext-workspace-v1 protocol name, and notifies all IPC watchers. The name may contain spaces. | + +*Example:* +```bash +mmsg rename tag 1 web +mmsg rename tag 2 my projects +``` + ### DISPATCH Allows sending commands to the compositor to alter its state. * `dispatch ,[args...] [client,]` diff --git a/mmsg/mmsg.1 b/mmsg/mmsg.1 index 2449e6e0..f4a3f16a 100644 --- a/mmsg/mmsg.1 +++ b/mmsg/mmsg.1 @@ -74,6 +74,24 @@ Examples: .IP \(bu 2 \fBmmsg dispatch movewin,10,100 client,4\fR .RE +.SS "Rename commands" +.TP +\fBrename tag\fR +Rename a tag at runtime. +\fIindex\fR is the 1-based tag number. +\fIname\fR may contain spaces. +The change is reflected immediately in the ext-workspace-v1 protocol and in all +active IPC watch streams. +Survives \fBreload_config\fR only if also set via \fBtagrule=id:,name:\fR +in the config file. +.br +Example: +.RS +.IP \(bu 2 +\fBmmsg rename tag 1 web\fR +.IP \(bu 2 +\fBmmsg rename tag 2 my projects\fR +.RE .SS "Persistent streams (watch)" \fBWatch\fR commands keep the connection open and continuously output JSON updates. The initial state is sent immediately, followed by updates whenever diff --git a/src/ext-protocol/ext-workspace.h b/src/ext-protocol/ext-workspace.h index 0c53cb11..c2230781 100644 --- a/src/ext-protocol/ext-workspace.h +++ b/src/ext-protocol/ext-workspace.h @@ -155,6 +155,8 @@ void dwl_ext_workspace_printstatus(Monitor *m) { wl_list_for_each(w, &workspaces, link) { if (w && w->m == m) { + wlr_ext_workspace_handle_v1_set_name(w->ext_workspace, + get_name_from_tag(w->tag)); tag_status = get_tag_status(w->tag, m); if (tag_status == 2) { wlr_ext_workspace_handle_v1_set_hidden(w->ext_workspace, false); diff --git a/src/ipc/ipc.h b/src/ipc/ipc.h index e66b2842..7f4eb09a 100644 --- a/src/ipc/ipc.h +++ b/src/ipc/ipc.h @@ -352,6 +352,41 @@ static void handle_command(int client_fd, const char *cmd_raw) { return; } resp = build_monitor_tags_response(m); + } else if (strncmp(cmd, "rename tag ", 11) == 0) { + char *rest = (char *)(cmd + 11); + char *endptr; + long tag_index = strtol(rest, &endptr, 10); + if (endptr == rest || *endptr != ' ' || + tag_index < 1 || tag_index > (int)LENGTH(tags)) { + send_static_json(client_fd, "{\"error\":\"usage: rename tag \"}\n"); + return; + } + const char *new_name = endptr + 1; + if (*new_name == '\0') { + send_static_json(client_fd, "{\"error\":\"usage: rename tag \"}\n"); + return; + } + bool found = false; + for (int i = 0; i < config.tag_rules_count; i++) { + if (config.tag_rules[i].id == (int)tag_index) { + free(config.tag_rules[i].name); + config.tag_rules[i].name = strdup(new_name); + found = true; + break; + } + } + if (!found) { + config.tag_rules = realloc(config.tag_rules, + (config.tag_rules_count + 1) * sizeof(ConfigTagRule)); + ConfigTagRule *rule = &config.tag_rules[config.tag_rules_count]; + memset(rule, 0, sizeof(ConfigTagRule)); + rule->id = (int)tag_index; + rule->name = strdup(new_name); + config.tag_rules_count++; + } + printstatus(IPC_WATCH_ARRANGGE); + resp = cJSON_CreateObject(); + cJSON_AddBoolToObject(resp, "success", true); } else if (strncmp(cmd, "dispatch ", 9) == 0) { char *dispatch_copy = strdup(cmd_raw + 9); char *out = dispatch_copy, *ptr = dispatch_copy; @@ -1060,4 +1095,4 @@ void ipc_cleanup(void) { struct ipc_watch_client *wc, *tmp; wl_list_for_each_safe(wc, tmp, &watch_clients, link) ipc_remove_watch_client(wc); -} \ No newline at end of file +}