diff --git a/docs/ipc.md b/docs/ipc.md index 18618da8..b4372c39 100644 --- a/docs/ipc.md +++ b/docs/ipc.md @@ -27,6 +27,7 @@ description: Control mangowm programmatically using mmsg. | `get keymode` | Returns the current active keyboard mode (e.g., normal, insert). | | `get keyboardlayout` | Returns the active XKB layout (abbreviated). | | `get monitor ` | Returns full JSON details for a specific monitor. | +| `get focusing-client` | Returns full JSON details for the client currently in focus. | | `get client ` | Returns full JSON details for a client with the given ID. | | `get tag ` | Queries status of a specific tag on a monitor. | | `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. * `watch monitor ` +* `watch focusing-client` * `watch client ` * `watch tags ` * `watch all-monitors` diff --git a/src/ipc/ipc.h b/src/ipc/ipc.h index e3f0120f..7d3c0660 100644 --- a/src/ipc/ipc.h +++ b/src/ipc/ipc.h @@ -20,6 +20,7 @@ enum ipc_watch_type { IPC_WATCH_KEYMODE, IPC_WATCH_KB_LAYOUT, IPC_WATCH_LAST_OPEN_SURFACE, + IPC_WATCH_FOCUSING_CLIENT }; struct ipc_watch_client { @@ -270,6 +271,13 @@ static void handle_command(int client_fd, const char *cmd_raw) { return; } 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) { Client *c = client_by_id((uint32_t)atoi(cmd + 11)); if (!c) { @@ -478,6 +486,8 @@ static bool handle_watch_command(int fd, const char *cmd, if (strncmp(cmd, "watch monitor ", 14) == 0) { type = IPC_WATCH_MONITOR; arg = cmd + 14; + } else if (strcmp(cmd, "watch focusing-client") == 0) { + type = IPC_WATCH_FOCUSING_CLIENT; } else if (strncmp(cmd, "watch client ", 13) == 0) { type = IPC_WATCH_CLIENT; client_id = (uint32_t)atoi(cmd + 13); @@ -541,6 +551,17 @@ static bool handle_watch_command(int fd, const char *cmd, } 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: { Client *c = client_by_id(client_id); if (c) @@ -729,6 +750,38 @@ void ipc_notify_last_surface_ws_name(Monitor *m) { 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) { char *json_str = NULL; size_t len = 0; diff --git a/src/mango.c b/src/mango.c index 1aaaf471..148c37b6 100644 --- a/src/mango.c +++ b/src/mango.c @@ -5710,6 +5710,7 @@ void handle_print_status(struct wl_listener *listener, void *data) { ipc_notify_keymode(); ipc_notify_kb_layout(); + ipc_notify_focusing_client(); ipc_notify_all_tags(); ipc_notify_all_clients(); ipc_notify_all_monitors();