feat: add focusing-client watch to mmsg

This commit is contained in:
DreamMaoMao 2026-05-26 10:24:16 +08:00
parent e502793ea1
commit 900b49526e
3 changed files with 56 additions and 0 deletions

View file

@ -27,6 +27,7 @@ description: Control mangowm programmatically using mmsg.
| `get keymode` | Returns the current active keyboard mode (e.g., normal, insert). | | `get keymode` | Returns the current active keyboard mode (e.g., normal, insert). |
| `get keyboardlayout` | Returns the active XKB layout (abbreviated). | | `get keyboardlayout` | Returns the active XKB layout (abbreviated). |
| `get monitor <name>` | Returns full JSON details for a specific monitor. | | `get monitor <name>` | Returns full JSON details for a specific monitor. |
| `get focusing-client` | Returns full JSON details for the client currently in focus. |
| `get client <id>` | Returns full JSON details for a client with the given ID. | | `get client <id>` | Returns full JSON details for a client with the given ID. |
| `get tag <mon> <idx>` | Queries status of a specific tag on a monitor. | | `get tag <mon> <idx>` | Queries status of a specific tag on a monitor. |
| `get all-clients` | Returns a JSON array of all active clients. | | `get all-clients` | Returns a JSON array of all active clients. |
@ -45,6 +46,7 @@ mmsg get all-monitors
Subscribes the client to real-time updates. When the state changes, the server pushes a new JSON object to the output stream. Subscribes the client to real-time updates. When the state changes, the server pushes a new JSON object to the output stream.
* `watch monitor <name>` * `watch monitor <name>`
* `watch focusing-client`
* `watch client <id>` * `watch client <id>`
* `watch tags <mon_name>` * `watch tags <mon_name>`
* `watch all-monitors` * `watch all-monitors`

View file

@ -20,6 +20,7 @@ enum ipc_watch_type {
IPC_WATCH_KEYMODE, IPC_WATCH_KEYMODE,
IPC_WATCH_KB_LAYOUT, IPC_WATCH_KB_LAYOUT,
IPC_WATCH_LAST_OPEN_SURFACE, IPC_WATCH_LAST_OPEN_SURFACE,
IPC_WATCH_FOCUSING_CLIENT
}; };
struct ipc_watch_client { struct ipc_watch_client {
@ -270,6 +271,13 @@ static void handle_command(int client_fd, const char *cmd_raw) {
return; return;
} }
resp = build_monitor_json(m); resp = build_monitor_json(m);
} else if (strcmp(cmd, "get focusing-client") == 0) {
if (selmon && selmon->sel) {
resp = build_client_json(selmon->sel);
} else {
send_static_json(client_fd, "{\"error\":\"no focused client\"}\n");
return;
}
} else if (strncmp(cmd, "get client ", 11) == 0) { } else if (strncmp(cmd, "get client ", 11) == 0) {
Client *c = client_by_id((uint32_t)atoi(cmd + 11)); Client *c = client_by_id((uint32_t)atoi(cmd + 11));
if (!c) { if (!c) {
@ -478,6 +486,8 @@ static bool handle_watch_command(int fd, const char *cmd,
if (strncmp(cmd, "watch monitor ", 14) == 0) { if (strncmp(cmd, "watch monitor ", 14) == 0) {
type = IPC_WATCH_MONITOR; type = IPC_WATCH_MONITOR;
arg = cmd + 14; arg = cmd + 14;
} else if (strcmp(cmd, "watch focusing-client") == 0) {
type = IPC_WATCH_FOCUSING_CLIENT;
} else if (strncmp(cmd, "watch client ", 13) == 0) { } else if (strncmp(cmd, "watch client ", 13) == 0) {
type = IPC_WATCH_CLIENT; type = IPC_WATCH_CLIENT;
client_id = (uint32_t)atoi(cmd + 13); client_id = (uint32_t)atoi(cmd + 13);
@ -541,6 +551,17 @@ static bool handle_watch_command(int fd, const char *cmd,
} }
break; break;
} }
case IPC_WATCH_FOCUSING_CLIENT: {
if (selmon && selmon->sel) {
json = build_client_json(selmon->sel);
} else {
json = cJSON_CreateObject();
cJSON_AddNullToObject(json, "id");
cJSON_AddNullToObject(json, "title");
cJSON_AddNullToObject(json, "appid");
}
break;
}
case IPC_WATCH_CLIENT: { case IPC_WATCH_CLIENT: {
Client *c = client_by_id(client_id); Client *c = client_by_id(client_id);
if (c) if (c)
@ -729,6 +750,38 @@ void ipc_notify_last_surface_ws_name(Monitor *m) {
free(json_str); free(json_str);
} }
void ipc_notify_focusing_client(void) {
char *json_str = NULL;
size_t len = 0;
struct ipc_watch_client *wc, *tmp;
wl_list_for_each_safe(wc, tmp, &watch_clients, link) {
if (wc->type == IPC_WATCH_FOCUSING_CLIENT) {
if (!json_str) {
cJSON *json = NULL;
if (selmon && selmon->sel) {
json = build_client_json(selmon->sel);
} else {
json = cJSON_CreateObject();
cJSON_AddNullToObject(json, "id");
cJSON_AddNullToObject(json, "title");
cJSON_AddNullToObject(json, "appid");
}
char *raw = cJSON_PrintUnformatted(json);
cJSON_Delete(json);
if (!raw)
return;
len = strlen(raw);
json_str = malloc(len + 2);
snprintf(json_str, len + 2, "%s\n", raw);
free(raw);
}
if (send(wc->fd, json_str, len + 1, MSG_NOSIGNAL) < 0)
ipc_remove_watch_client(wc);
}
}
free(json_str);
}
void ipc_notify_client(Client *c) { void ipc_notify_client(Client *c) {
char *json_str = NULL; char *json_str = NULL;
size_t len = 0; size_t len = 0;

View file

@ -5710,6 +5710,7 @@ void handle_print_status(struct wl_listener *listener, void *data) {
ipc_notify_keymode(); ipc_notify_keymode();
ipc_notify_kb_layout(); ipc_notify_kb_layout();
ipc_notify_focusing_client();
ipc_notify_all_tags(); ipc_notify_all_tags();
ipc_notify_all_clients(); ipc_notify_all_clients();
ipc_notify_all_monitors(); ipc_notify_all_monitors();