#define _GNU_SOURCE #include #include #include #include #include #include #include static void usage(void) { printf("Usage: mmsg [args...]\n\n"); printf("One-shot queries (get):\n"); printf( " get version Show compositor version\n"); printf(" get keymode Show current keymode\n"); printf(" get keyboardlayout Show current keyboard " "layout\n"); printf(" get last_open_surface [monitor] Show last open surface " "(default focused monitor)\n"); printf(" get monitor Show monitor details\n"); printf(" get focusing-client Show focused client " "details\n"); printf(" get client Show client details by " "ID\n"); printf(" get tag Show tag details " "(1‑based index)\n"); printf(" get all-clients List all clients\n"); printf(" get all-monitors List all monitors\n"); printf(" get all-tags List all tags (all " "monitors)\n"); printf( " get tags List tags for a monitor\n"); printf(" dispatch [,arg...] [client,] Call a compositor " "function\n"); printf(" and arguments are separated by commas.\n"); printf(" Add 'client,' at the beginning or end to target a " "specific client.\n"); printf(" Examples:\n"); printf(" dispatch togglefloating\n"); printf(" dispatch movewin,10,100\n"); printf(" dispatch movewin,10,100 client,4\n"); printf("Persistent streams (watch):\n"); printf( " watch monitor Stream monitor changes\n"); printf(" watch focusing-client Stream focused client " "changes\n"); printf( " watch client Stream client changes\n"); printf(" watch tags Stream tag changes for " "a monitor\n"); printf(" watch all-monitors Stream all monitors " "changes\n"); printf( " watch all-tags Stream all tags changes\n"); printf(" watch all-clients Stream all clients " "changes\n"); printf( " watch keymode Stream keymode changes\n"); printf(" watch keyboardlayout Stream keyboard layout " "changes\n"); printf(" watch last_open_surface [monitor] Stream last open " "surface changes\n\n"); printf("Environment:\n"); printf(" MANGO_INSTANCE_SIGNATURE IPC socket path (set by the " "compositor)\n\n"); printf("Run 'mmsg --help', '-h' or 'help' to see this message.\n"); } int main(int argc, char *argv[]) { if (argc >= 2 && (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "help") == 0)) { usage(); return EXIT_SUCCESS; } if (argc < 2) { fprintf(stderr, "Usage: mmsg [args...]\n"); fprintf(stderr, " get ... one-shot request\n"); fprintf(stderr, " watch ... persistent stream\n"); return EXIT_FAILURE; } const char *socket_path = getenv("MANGO_INSTANCE_SIGNATURE"); if (!socket_path) { fprintf(stderr, "Error: MANGO_INSTANCE_SIGNATURE is not set. Did you " "run 'mmsg' in mango?\n"); return EXIT_FAILURE; } int sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) { perror("socket"); return EXIT_FAILURE; } struct sockaddr_un addr = {.sun_family = AF_UNIX}; strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("connect"); close(sock); return EXIT_FAILURE; } char cmd[4096] = {0}; int offset = 0; for (int i = 1; i < argc; i++) { int n = snprintf(cmd + offset, sizeof(cmd) - offset, "%s%s", argv[i], (i == argc - 1) ? "" : " "); if (n < 0 || n >= (int)(sizeof(cmd) - offset)) { fprintf(stderr, "Error: command too long.\n"); close(sock); return EXIT_FAILURE; } offset += n; } int n = snprintf(cmd + offset, sizeof(cmd) - offset, "\n"); if (n < 0 || n >= (int)(sizeof(cmd) - offset)) { fprintf(stderr, "Error: command too long to append newline.\n"); close(sock); return EXIT_FAILURE; } if (send(sock, cmd, strlen(cmd), MSG_NOSIGNAL) < 0) { perror("send"); close(sock); return EXIT_FAILURE; } FILE *stream = fdopen(sock, "r"); if (!stream) { perror("fdopen"); close(sock); return EXIT_FAILURE; } char *line = NULL; size_t len = 0; while (getline(&line, &len, stream) != -1) { printf("%s", line); fflush(stdout); } if (ferror(stream)) { perror("recv"); free(line); fclose(stream); return EXIT_FAILURE; } free(line); fclose(stream); return EXIT_SUCCESS; }