This commit is contained in:
Emilia Miki 2026-06-29 23:13:00 +00:00 committed by GitHub
commit c8b7482202
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 87 additions and 4 deletions

View file

@ -67,6 +67,17 @@ mmsg watch all-monitors
mmsg watch all-tags mmsg watch all-tags
``` ```
### RENAME
| Command | Description |
| :--- | :--- |
| `rename tag <index> <name>` | 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 ### DISPATCH
Allows sending commands to the compositor to alter its state. Allows sending commands to the compositor to alter its state.
* `dispatch <func_name>,[args...] [client,<id>]` * `dispatch <func_name>,[args...] [client,<id>]`

View file

@ -74,6 +74,24 @@ Examples:
.IP \(bu 2 .IP \(bu 2
\fBmmsg dispatch movewin,10,100 client,4\fR \fBmmsg dispatch movewin,10,100 client,4\fR
.RE .RE
.SS "Rename commands"
.TP
\fBrename tag\fR <index> <name>
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:<index>,name:<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)" .SS "Persistent streams (watch)"
\fBWatch\fR commands keep the connection open and continuously output JSON \fBWatch\fR commands keep the connection open and continuously output JSON
updates. The initial state is sent immediately, followed by updates whenever updates. The initial state is sent immediately, followed by updates whenever

View file

@ -160,6 +160,7 @@ typedef struct {
typedef struct { typedef struct {
int32_t id; int32_t id;
char *name;
char *layout_name; char *layout_name;
char *monitor_name; char *monitor_name;
char *monitor_make; char *monitor_make;
@ -2183,6 +2184,7 @@ bool parse_option(Config *config, char *key, char *value) {
// 设置默认值 // 设置默认值
rule->id = 0; rule->id = 0;
rule->name = NULL;
rule->layout_name = NULL; rule->layout_name = NULL;
rule->monitor_name = NULL; rule->monitor_name = NULL;
rule->monitor_make = NULL; rule->monitor_make = NULL;
@ -2211,6 +2213,8 @@ bool parse_option(Config *config, char *key, char *value) {
if (strcmp(key, "id") == 0) { if (strcmp(key, "id") == 0) {
rule->id = CLAMP_INT(atoi(val), 0, LENGTH(tags)); 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) { } else if (strcmp(key, "layout_name") == 0) {
rule->layout_name = strdup(val); rule->layout_name = strdup(val);
} else if (strcmp(key, "monitor_name") == 0) { } else if (strcmp(key, "monitor_name") == 0) {
@ -3300,6 +3304,8 @@ void free_config(void) {
// 释放 tag_rules // 释放 tag_rules
if (config.tag_rules) { if (config.tag_rules) {
for (int32_t i = 0; i < config.tag_rules_count; i++) { 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) if (config.tag_rules[i].layout_name)
free((void *)config.tag_rules[i].layout_name); free((void *)config.tag_rules[i].layout_name);
if (config.tag_rules[i].monitor_name) if (config.tag_rules[i].monitor_name)
@ -4302,3 +4308,13 @@ int32_t reload_config(const Arg *arg) {
printstatus(IPC_WATCH_ARRANGGE); printstatus(IPC_WATCH_ARRANGGE);
return 1; 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;
}

View file

@ -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 *get_name_from_tag(uint32_t tag) {
static const char *names[] = {"overview", "1", "2", "3", "4", if (tag == 0)
"5", "6", "7", "8", "9"}; return "overview";
return (tag < sizeof(names) / sizeof(names[0])) ? names[tag] : NULL; return get_tag_name((int)tag);
} }
void destroy_workspace(struct workspace *workspace) { void destroy_workspace(struct workspace *workspace) {
@ -155,6 +155,8 @@ void dwl_ext_workspace_printstatus(Monitor *m) {
wl_list_for_each(w, &workspaces, link) { wl_list_for_each(w, &workspaces, link) {
if (w && w->m == m) { 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); tag_status = get_tag_status(w->tag, m);
if (tag_status == 2) { if (tag_status == 2) {
wlr_ext_workspace_handle_v1_set_hidden(w->ext_workspace, false); wlr_ext_workspace_handle_v1_set_hidden(w->ext_workspace, false);

View file

@ -99,6 +99,7 @@ static cJSON *build_tags_json(Monitor *m) {
} }
cJSON *tag_obj = cJSON_CreateObject(); cJSON *tag_obj = cJSON_CreateObject();
cJSON_AddNumberToObject(tag_obj, "index", tag); 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_active", is_active);
cJSON_AddBoolToObject(tag_obj, "is_urgent", is_urgent); cJSON_AddBoolToObject(tag_obj, "is_urgent", is_urgent);
cJSON_AddStringToObject(tag_obj, "layout", cJSON_AddStringToObject(tag_obj, "layout",
@ -351,6 +352,41 @@ static void handle_command(int client_fd, const char *cmd_raw) {
return; return;
} }
resp = build_monitor_tags_response(m); 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 <index> <name>\"}\n");
return;
}
const char *new_name = endptr + 1;
if (*new_name == '\0') {
send_static_json(client_fd, "{\"error\":\"usage: rename tag <index> <name>\"}\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) { } else if (strncmp(cmd, "dispatch ", 9) == 0) {
char *dispatch_copy = strdup(cmd_raw + 9); char *dispatch_copy = strdup(cmd_raw + 9);
char *out = dispatch_copy, *ptr = dispatch_copy; char *out = dispatch_copy, *ptr = dispatch_copy;