From 324d29e68cdda075574255460f6faa99dc4c0c40 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sun, 24 May 2026 13:31:31 +0800 Subject: [PATCH] feat: new ipc impl --- format.sh | 2 +- meson.build | 2 + mmsg/arg.h | 65 --- mmsg/dynarr.h | 30 -- mmsg/mmsg.c | 804 +++--------------------------- src/action/client.h | 5 +- src/config/parse_config.h | 6 + src/dispatch/bind_declare.h | 3 +- src/dispatch/bind_define.h | 266 ++++------ src/ext-protocol/dwl-ipc.h | 2 +- src/fetch/client.h | 8 +- src/ipc/ipc.h | 970 ++++++++++++++++++++++++++++++++++++ src/mango.c | 62 +-- 13 files changed, 1205 insertions(+), 1020 deletions(-) delete mode 100644 mmsg/arg.h delete mode 100644 mmsg/dynarr.h create mode 100644 src/ipc/ipc.h diff --git a/format.sh b/format.sh index 1291ff8f..6b204b00 100644 --- a/format.sh +++ b/format.sh @@ -1,3 +1,3 @@ #!/usr/bin/bash -clang-format -i src/*/*.h -i src/*/*.c -i src/mango.c -i mmsg/mmsg.c -i mmsg/arg.h -i mmsg/dynarr.h +clang-format -i src/*/*.h -i src/*/*.c -i src/mango.c -i mmsg/mmsg.c diff --git a/meson.build b/meson.build index 36ba100c..8cb137fd 100644 --- a/meson.build +++ b/meson.build @@ -39,6 +39,7 @@ libinput_dep = dependency('libinput',version: '>=1.27.1') libwayland_client_dep = dependency('wayland-client') pcre2_dep = dependency('libpcre2-8') pixman_dep = dependency('pixman-1') +cjson_dep = dependency('libcjson') # 获取版本信息 @@ -109,6 +110,7 @@ executable('mango', libwayland_client_dep, pcre2_dep, pixman_dep, + cjson_dep, ], install : true, c_args : c_args, diff --git a/mmsg/arg.h b/mmsg/arg.h deleted file mode 100644 index c3b0d7b9..00000000 --- a/mmsg/arg.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copy me if you can. - * by 20h - */ - -#ifndef ARG_H__ -#define ARG_H__ - -extern char *argv0; - -/* use main(int32_t argc, char *argv[]) */ -#define ARGBEGIN \ - for (argv0 = *argv, argv++, argc--; \ - argv[0] && argv[0][0] == '-' && argv[0][1]; argc--, argv++) { \ - char argc_; \ - char **argv_; \ - int32_t brk_; \ - if (argv[0][1] == '-' && argv[0][2] == '\0') { \ - argv++; \ - argc--; \ - break; \ - } \ - for (brk_ = 0, argv[0]++, argv_ = argv; argv[0][0] && !brk_; \ - argv[0]++) { \ - if (argv_ != argv) \ - break; \ - argc_ = argv[0][0]; \ - switch (argc_) - -/* Handles obsolete -NUM syntax */ -#define ARGNUM \ - case '0': \ - case '1': \ - case '2': \ - case '3': \ - case '4': \ - case '5': \ - case '6': \ - case '7': \ - case '8': \ - case '9' - -#define ARGEND \ - } \ - } - -#define ARGC() argc_ - -#define ARGNUMF() (brk_ = 1, estrtonum(argv[0], 0, INT_MAX)) - -#define EARGF(x) \ - ((argv[0][1] == '\0' && argv[1] == NULL) \ - ? ((x), abort(), (char *)0) \ - : (brk_ = 1, \ - (argv[0][1] != '\0') ? (&argv[0][1]) : (argc--, argv++, argv[0]))) - -#define ARGF() \ - ((argv[0][1] == '\0' && argv[1] == NULL) \ - ? (char *)0 \ - : (brk_ = 1, \ - (argv[0][1] != '\0') ? (&argv[0][1]) : (argc--, argv++, argv[0]))) - -#define LNGARG() &argv[0][0] - -#endif diff --git a/mmsg/dynarr.h b/mmsg/dynarr.h deleted file mode 100644 index 45cd356a..00000000 --- a/mmsg/dynarr.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef DYNARR_H__ -#define DYNARR_H__ - -#define DYNARR_DEF(t) \ - struct { \ - t *arr; \ - size_t len, cap, size; \ - } - -#define DYNARR_INIT(p) \ - ((p)->arr = reallocarray((p)->arr, ((p)->cap = 1), \ - ((p)->size = sizeof(((p)->arr[0]))))) - -#define DYNARR_FINI(p) free((p)->arr) - -#define DYNARR_PUSH(p, v) \ - do { \ - if ((p)->len >= (p)->cap) { \ - while ((p)->len >= ((p)->cap *= 2)) \ - ; \ - (p)->arr = reallocarray((p)->arr, (p)->cap, (p)->size); \ - } \ - (p)->arr[(p)->len++] = (v); \ - } while (0) - -#define DYNARR_POP(p) ((p)->arr[(p)->len--]) - -#define DYNARR_CLR(p) ((p)->len = 0) - -#endif diff --git a/mmsg/mmsg.c b/mmsg/mmsg.c index 69f1d1d0..10b24afc 100644 --- a/mmsg/mmsg.c +++ b/mmsg/mmsg.c @@ -1,754 +1,96 @@ -#include "arg.h" -#include "dwl-ipc-unstable-v2-protocol.h" -#include "dynarr.h" -#include -#include +#define _GNU_SOURCE +#include #include #include #include +#include +#include #include -#include -#include -#define die(fmt, ...) \ - do { \ - fprintf(stderr, fmt "\n", ##__VA_ARGS__); \ - exit(EXIT_FAILURE); \ - } while (0) - -char *argv0; - -static enum { - NONE = 0, - SET = 1 << 0, - GET = 1 << 1, - WATCH = 1 << 2 | GET, -} mode = NONE; - -static int32_t Oflag; -static int32_t Tflag; -static int32_t Lflag; -static int32_t oflag; -static int32_t tflag; -static int32_t lflag; -static int32_t cflag; -static int32_t vflag; -static int32_t mflag; -static int32_t fflag; -static int32_t qflag; -static int32_t dflag; -static int32_t xflag; -static int32_t eflag; -static int32_t kflag; -static int32_t bflag; -static int32_t Aflag; - -static uint32_t occ, seltags, total_clients, urg; - -static char *output_name; -static int32_t tagcount; -static char *tagset; -static char *layout_name; -static int32_t layoutcount, layout_idx; -static char *client_tags; -static char *dispatch_cmd; -static char *dispatch_arg1; -static char *dispatch_arg2; -static char *dispatch_arg3; -static char *dispatch_arg4; -static char *dispatch_arg5; - -struct output { - char *output_name; - uint32_t name; -}; -static DYNARR_DEF(struct output) outputs; - -static struct wl_display *display; -static struct zdwl_ipc_manager_v2 *dwl_ipc_manager; - -// 为每个回调定义专用的空函数 -static void noop_geometry(void *data, struct wl_output *wl_output, int32_t x, - int32_t y, int32_t physical_width, - int32_t physical_height, int32_t subpixel, - const char *make, const char *model, - int32_t transform) {} - -static void noop_mode(void *data, struct wl_output *wl_output, uint32_t flags, - int32_t width, int32_t height, int32_t refresh) {} - -static void noop_done(void *data, struct wl_output *wl_output) {} - -static void noop_scale(void *data, struct wl_output *wl_output, - int32_t factor) {} - -static void noop_description(void *data, struct wl_output *wl_output, - const char *description) {} - -// 将 n 转换为 9 位二进制字符串,结果存入 buf(至少长度 10) -void bin_str_9bits(char *buf, uint32_t n) { - for (int32_t i = 8; i >= 0; i--) { - *buf++ = ((n >> i) & 1) ? '1' : '0'; +int main(int argc, char *argv[]) { + 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; } - *buf = '\0'; // 字符串结尾 -} -static void dwl_ipc_tags(void *data, - struct zdwl_ipc_manager_v2 *dwl_ipc_manager, - uint32_t count) { - tagcount = count; - if (Tflag && mode & GET) - printf("%d\n", tagcount); -} - -static void dwl_ipc_layout(void *data, - struct zdwl_ipc_manager_v2 *dwl_ipc_manager, - const char *name) { - if (lflag && mode & SET && strcmp(layout_name, name) == 0) - layout_idx = layoutcount; - if (Lflag && mode & GET) - printf("%s\n", name); - layoutcount++; -} - -static const struct zdwl_ipc_manager_v2_listener dwl_ipc_listener = { - .tags = dwl_ipc_tags, .layout = dwl_ipc_layout}; - -static void -dwl_ipc_output_toggle_visibility(void *data, - struct zdwl_ipc_output_v2 *dwl_ipc_output) { - if (!vflag) - return; - char *output_name = data; - if (output_name) - printf("%s ", output_name); - printf("toggle\n"); -} - -static void dwl_ipc_output_active(void *data, - struct zdwl_ipc_output_v2 *dwl_ipc_output, - uint32_t active) { - if (!oflag) { - if (mode & SET && !output_name && active) - output_name = strdup(data); - return; + 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; } - char *output_name = data; - if (output_name) - printf("%s ", output_name); - printf("selmon %u\n", active ? 1 : 0); -} -static void dwl_ipc_output_tag(void *data, - struct zdwl_ipc_output_v2 *dwl_ipc_output, - uint32_t tag, uint32_t state, uint32_t clients, - uint32_t focused) { - if (!tflag) - return; - if (state == ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE) - seltags |= 1 << tag; - if (state == ZDWL_IPC_OUTPUT_V2_TAG_STATE_URGENT) - urg |= 1 << tag; - if (clients > 0) - occ |= 1 << tag; - - // 累计所有 tag 的 clients 总数 - total_clients += clients; - - if (!(mode & GET)) - return; - char *output_name = data; - if (output_name) - printf("%s ", output_name); - printf("tag %u %u %u %u\n", tag + 1, state, clients, focused); -} - -static void dwl_ipc_output_layout(void *data, - struct zdwl_ipc_output_v2 *dwl_ipc_output, - uint32_t layout) {} - -static void dwl_ipc_output_layout_symbol( - void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, const char *layout) { - if (!(lflag && mode & GET)) - return; - char *output_name = data; - if (output_name) - printf("%s ", output_name); - printf("layout %s\n", layout); -} - -static void dwl_ipc_output_title(void *data, - struct zdwl_ipc_output_v2 *dwl_ipc_output, - const char *title) { - if (!(cflag && mode & GET)) - return; - char *output_name = data; - if (output_name) - printf("%s ", output_name); - printf("title %s\n", title); -} - -static void dwl_ipc_output_appid(void *data, - struct zdwl_ipc_output_v2 *dwl_ipc_output, - const char *appid) { - if (!(cflag && mode & GET)) - return; - char *output_name = data; - if (output_name) - printf("%s ", output_name); - printf("appid %s\n", appid); -} - -static void dwl_ipc_output_x(void *data, - struct zdwl_ipc_output_v2 *dwl_ipc_output, - int32_t x) { - if (!xflag) - return; - char *output_name = data; - if (output_name) - printf("%s ", output_name); - printf("x %d\n", x); -} - -static void dwl_ipc_output_y(void *data, - struct zdwl_ipc_output_v2 *dwl_ipc_output, - int32_t y) { - if (!xflag) - return; - char *output_name = data; - if (output_name) - printf("%s ", output_name); - printf("y %d\n", y); -} - -static void dwl_ipc_output_width(void *data, - struct zdwl_ipc_output_v2 *dwl_ipc_output, - int32_t width) { - if (!xflag) - return; - char *output_name = data; - if (output_name) - printf("%s ", output_name); - printf("width %d\n", width); -} - -static void dwl_ipc_output_height(void *data, - struct zdwl_ipc_output_v2 *dwl_ipc_output, - int32_t height) { - if (!xflag) - return; - char *output_name = data; - if (output_name) - printf("%s ", output_name); - printf("height %d\n", height); -} - -static void dwl_ipc_output_last_layer(void *data, - struct zdwl_ipc_output_v2 *dwl_ipc_output, - const char *last_layer) { - if (!eflag) - return; - char *output_name = data; - if (output_name) - printf("%s ", output_name); - printf("last_layer %s\n", last_layer); -} - -static void dwl_ipc_output_kb_layout(void *data, - struct zdwl_ipc_output_v2 *dwl_ipc_output, - const char *kb_layout) { - if (!kflag) - return; - char *output_name = data; - if (output_name) - printf("%s ", output_name); - printf("kb_layout %s\n", kb_layout); -} - -static void -dwl_ipc_output_scalefactor(void *data, - struct zdwl_ipc_output_v2 *dwl_ipc_output, - const uint32_t scalefactor) { - if (!Aflag) - return; - char *output_name = data; - if (output_name) - printf("%s ", output_name); - printf("scale_factor %f\n", scalefactor / 100.0f); -} - -static void dwl_ipc_output_keymode(void *data, - struct zdwl_ipc_output_v2 *dwl_ipc_output, - const char *keymode) { - if (!bflag) - return; - char *output_name = data; - if (output_name) - printf("%s ", output_name); - printf("keymode %s\n", keymode); -} - -static void dwl_ipc_output_fullscreen(void *data, - struct zdwl_ipc_output_v2 *dwl_ipc_output, - uint32_t is_fullscreen) { - if (!mflag) - return; - char *output_name = data; - if (output_name) - printf("%s ", output_name); - printf("fullscreen %u\n", is_fullscreen); -} - -static void dwl_ipc_output_floating(void *data, - struct zdwl_ipc_output_v2 *dwl_ipc_output, - uint32_t is_floating) { - if (!fflag) - return; - char *output_name = data; - if (output_name) - printf("%s ", output_name); - printf("floating %u\n", is_floating); -} - -static void dwl_ipc_output_frame(void *data, - struct zdwl_ipc_output_v2 *dwl_ipc_output) { - if (mode & SET) { - if (data && (!output_name || strcmp(output_name, (char *)data))) - return; - if (qflag) { - zdwl_ipc_output_v2_quit(dwl_ipc_output); - } - if (lflag) { - zdwl_ipc_output_v2_set_layout(dwl_ipc_output, layout_idx); - } - if (tflag) { - uint32_t mask = seltags; - char *t = tagset; - int32_t i = 0; - - for (; *t && *t >= '0' && *t <= '9'; t++) - i = *t - '0' + i * 10; - - if (!*t) - mask = 1 << (i - 1); - - for (; *t; t++, i++) { - switch (*t) { - case '-': - mask &= ~(1 << (i - 1)); - break; - case '+': - mask |= 1 << (i - 1); - break; - case '^': - mask ^= 1 << (i - 1); - break; - } - } - - if ((i - 1) > tagcount) - die("bad tagset %s", tagset); - - zdwl_ipc_output_v2_set_tags(dwl_ipc_output, mask, 0); - } - if (cflag) { - uint32_t and = ~0, xor = 0; - char *t = client_tags; - int32_t i = 0; - - for (; *t && *t >= '0' && *t <= '9'; t++) - i = *t - '0' + i * 10; - - if (!*t) - t = "+"; - - for (; *t; t++, i++) { - switch (*t) { - case '-': - and &= ~(1 << (i - 1)); - break; - case '+': - and &= ~(1 << (i - 1)); - xor |= 1 << (i - 1); - break; - case '^': - xor |= 1 << (i - 1); - break; - } - } - if ((i - 1) > tagcount) - die("bad client tagset %s", client_tags); - - zdwl_ipc_output_v2_set_client_tags(dwl_ipc_output, and, xor); - } - if (dflag) { - zdwl_ipc_output_v2_dispatch( - dwl_ipc_output, dispatch_cmd, dispatch_arg1, dispatch_arg2, - dispatch_arg3, dispatch_arg4, dispatch_arg5); - } - wl_display_flush(display); - usleep(1000); - exit(0); - } else { - if (tflag) { - char *output_name = data; - - printf("%s clients %u\n", output_name, total_clients); - - char occ_str[10], seltags_str[10], urg_str[10]; - - bin_str_9bits(occ_str, occ); - bin_str_9bits(seltags_str, seltags); - bin_str_9bits(urg_str, urg); - printf("%s tags %u %u %u\n", output_name, occ, seltags, urg); - printf("%s tags %s %s %s\n", output_name, occ_str, seltags_str, - urg_str); - occ = seltags = total_clients = urg = 0; - } + int sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + perror("socket"); + return EXIT_FAILURE; } - fflush(stdout); -} -static const struct zdwl_ipc_output_v2_listener dwl_ipc_output_listener = { - .toggle_visibility = dwl_ipc_output_toggle_visibility, - .active = dwl_ipc_output_active, - .tag = dwl_ipc_output_tag, - .layout = dwl_ipc_output_layout, - .title = dwl_ipc_output_title, - .appid = dwl_ipc_output_appid, - .layout_symbol = dwl_ipc_output_layout_symbol, - .fullscreen = dwl_ipc_output_fullscreen, - .floating = dwl_ipc_output_floating, - .x = dwl_ipc_output_x, - .y = dwl_ipc_output_y, - .width = dwl_ipc_output_width, - .height = dwl_ipc_output_height, - .last_layer = dwl_ipc_output_last_layer, - .kb_layout = dwl_ipc_output_kb_layout, - .keymode = dwl_ipc_output_keymode, - .scalefactor = dwl_ipc_output_scalefactor, - .frame = dwl_ipc_output_frame, -}; + struct sockaddr_un addr = {.sun_family = AF_UNIX}; + strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); -static void wl_output_name(void *data, struct wl_output *output, - const char *name) { - if (outputs.arr) { - struct output *o = (struct output *)data; - o->output_name = strdup(name); - printf("+ "); + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("connect"); + close(sock); + return EXIT_FAILURE; } - if (Oflag) - printf("%s\n", name); - if (output_name && strcmp(output_name, name) != 0) { - wl_output_release(output); - return; + + // 拼接命令,缓冲区大小 4096 以容纳较长参数 + 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; } - struct zdwl_ipc_output_v2 *dwl_ipc_output = - zdwl_ipc_manager_v2_get_output(dwl_ipc_manager, output); - zdwl_ipc_output_v2_add_listener(dwl_ipc_output, &dwl_ipc_output_listener, - output_name ? NULL : strdup(name)); -} -static const struct wl_output_listener output_listener = { - .geometry = noop_geometry, - .mode = noop_mode, - .done = noop_done, - .scale = noop_scale, - .name = wl_output_name, - .description = noop_description, -}; - -static void global_add(void *data, struct wl_registry *wl_registry, - uint32_t name, const char *interface, uint32_t version) { - if (strcmp(interface, wl_output_interface.name) == 0) { - struct wl_output *o = - wl_registry_bind(wl_registry, name, &wl_output_interface, - WL_OUTPUT_NAME_SINCE_VERSION); - if (!outputs.arr) { - wl_output_add_listener(o, &output_listener, NULL); - } else { - DYNARR_PUSH(&outputs, (struct output){.name = name}); - wl_output_add_listener(o, &output_listener, - &outputs.arr[outputs.len - 1]); - } - } else if (strcmp(interface, zdwl_ipc_manager_v2_interface.name) == 0) { - dwl_ipc_manager = wl_registry_bind(wl_registry, name, - &zdwl_ipc_manager_v2_interface, 2); - zdwl_ipc_manager_v2_add_listener(dwl_ipc_manager, &dwl_ipc_listener, - NULL); + // 添加换行符 + 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; } -} -static void global_remove(void *data, struct wl_registry *wl_registry, - uint32_t name) { - if (!outputs.arr) - return; - struct output *o = outputs.arr; - for (size_t i = 0; i < outputs.len; i++, o++) { - if (o->name == name) { - printf("- %s\n", o->output_name); - free(o->output_name); - *o = DYNARR_POP(&outputs); - } + // 发送命令,使用 MSG_NOSIGNAL 避免 SIGPIPE + if (send(sock, cmd, strlen(cmd), MSG_NOSIGNAL) < 0) { + perror("send"); + close(sock); + return EXIT_FAILURE; } -} -static const struct wl_registry_listener registry_listener = { - .global = global_add, - .global_remove = global_remove, -}; - -static void usage(void) { - fprintf(stderr, - "mmsg - MangoWM IPC\n" - "\n" - "SYNOPSIS:\n" - "\tmmsg [-OTLq]\n" - "\tmmsg [-o ] -s [-t ] [-l ] [-c ] [-d " - ",,,,,]\n" - "\tmmsg [-o ] (-g | -w) [-OotlcvmfxekbA]\n" - "\n" - "OPERATION MODES:\n" - "\t-g Get values (tags, layout, focused client)\n" - "\t-s Set values (switch tags, layouts)\n" - "\t-w Watch mode (stream events)\n" - "\n" - "GENERAL OPTIONS:\n" - "\t-O Get all output (monitor) information\n" - "\t-T Get number of tags\n" - "\t-L Get all available layouts\n" - "\t-q Quit mango\n" - "\t-o Select output (monitor)\n" - "\n" - "GET OPTIONS (used with -g or -w):\n" - "\t-O Get output name\n" - "\t-o Get output (monitor) focus information\n" - "\t-t Get selected tags\n" - "\t-l Get current layout\n" - "\t-c Get title and appid of focused clients\n" - "\t-v Get visibility of statusbar\n" - "\t-m Get fullscreen status\n" - "\t-f Get floating status\n" - "\t-x Get focused client geometry\n" - "\t-e Get name of last focused layer\n" - "\t-k Get current keyboard layout\n" - "\t-b Get current keybind mode\n" - "\t-A Get scale factor of monitor\n" - "\n" - "SET OPTIONS (used with -s):\n" - "\t-o Select output (monitor)\n" - "\t-t Set selected tags (can be used with [+-^.] " - "modifiers)\n" - "\t-l Set current layout\n" - "\t-c Get title and appid of focused client\n" - "\t-d , Dispatch internal command (max 5 args)\n"); - exit(2); -} - -int32_t main(int32_t argc, char *argv[]) { - ARGBEGIN { - case 'q': - qflag = 1; - if (!(mode & GET)) { - mode = SET; - } - break; - case 's': - if (mode != NONE) - usage(); - mode = SET; - break; - case 'g': - if (mode != NONE) - usage(); - mode = GET; - break; - case 'w': - if (mode != NONE) - usage(); - mode = WATCH; - break; - case 'o': - if (mode == GET || mode == WATCH) - oflag = 1; - else if (mode == SET) - output_name = EARGF(usage()); - else - output_name = ARGF(); - break; - case 't': - tflag = 1; - if (!(mode & GET)) { - mode = SET; - tagset = EARGF(usage()); - } - break; - case 'l': - lflag = 1; - if (!(mode & GET)) { - mode = SET; - layout_name = EARGF(usage()); - } - break; - case 'c': - cflag = 1; - if (!(mode & GET)) { - mode = SET; - client_tags = EARGF(usage()); - } - break; - case 'd': - dflag = 1; - if (!(mode & GET)) { - mode = SET; - char *arg = EARGF(usage()); - - dispatch_cmd = dispatch_arg1 = dispatch_arg2 = dispatch_arg3 = - dispatch_arg4 = dispatch_arg5 = ""; - - char *tokens[6] = {0}; - int count = 0; - - while (arg && count < 6) { - char *comma = (count < 5) ? strchr(arg, ',') : NULL; - if (comma) { - *comma = '\0'; - tokens[count++] = arg; - arg = comma + 1; - } else { - tokens[count++] = arg; - break; - } - } - - for (int i = 0; i < count; i++) { - char *str = tokens[i]; - while (isspace((unsigned char)*str)) - str++; - if (*str) { - char *end = str + strlen(str) - 1; - while (end > str && isspace((unsigned char)*end)) - end--; - *(end + 1) = '\0'; - } - tokens[i] = str; - } - - if (count > 0) - dispatch_cmd = tokens[0]; - if (count > 1) - dispatch_arg1 = tokens[1]; - if (count > 2) - dispatch_arg2 = tokens[2]; - if (count > 3) - dispatch_arg3 = tokens[3]; - if (count > 4) - dispatch_arg4 = tokens[4]; - if (count > 5) - dispatch_arg5 = tokens[5]; - } - break; - case 'O': - Oflag = 1; - if (mode && !(mode & GET)) - usage(); - if (mode & WATCH) - DYNARR_INIT(&outputs); - else - mode = GET; - break; - case 'T': - Tflag = 1; - if (mode && mode != GET) - usage(); - mode = GET; - break; - case 'L': - Lflag = 1; - if (mode && mode != GET) - usage(); - mode = GET; - break; - case 'v': - vflag = 1; - if (mode == SET) - usage(); - mode |= GET; - break; - case 'm': - mflag = 1; - if (mode == SET) - usage(); - mode |= GET; - break; - case 'f': - fflag = 1; - if (mode == SET) - usage(); - mode |= GET; - break; - case 'x': - xflag = 1; - if (mode == SET) - usage(); - mode |= GET; - break; - case 'e': - eflag = 1; - if (mode == SET) - usage(); - mode |= GET; - break; - case 'k': - kflag = 1; - if (mode == SET) - usage(); - mode |= GET; - break; - case 'b': - bflag = 1; - if (mode == SET) - usage(); - mode |= GET; - break; - case 'A': - Aflag = 1; - if (mode == SET) - usage(); - mode |= GET; - break; - default: - fprintf(stderr, "bad option %c\n", ARGC()); - usage(); + // 将 socket 封装为行缓冲文件流,自动处理 TCP 拆包,按完整行读取 + FILE *stream = fdopen(sock, "r"); + if (!stream) { + perror("fdopen"); + close(sock); + return EXIT_FAILURE; } - ARGEND - if (mode == NONE) - usage(); - if (mode & GET && !output_name && - !(oflag || tflag || lflag || Oflag || Tflag || Lflag || cflag || - vflag || mflag || fflag || xflag || eflag || kflag || bflag || - Aflag || dflag)) - oflag = tflag = lflag = cflag = vflag = mflag = fflag = xflag = eflag = - kflag = bflag = Aflag = 1; - display = wl_display_connect(NULL); - if (!display) - die("bad display"); + // 按行读取并输出,直到连接关闭(get 模式服务端主动 close)或出错 + char *line = NULL; + size_t len = 0; + while (getline(&line, &len, stream) != -1) { + printf("%s", line); + fflush(stdout); + } - struct wl_registry *registry = wl_display_get_registry(display); - wl_registry_add_listener(registry, ®istry_listener, NULL); + // 检查是否因读取错误退出(而非正常 EOF) + if (ferror(stream)) { + perror("recv"); + free(line); + fclose(stream); // 关闭 stream 同时关闭 socket + return EXIT_FAILURE; + } - wl_display_dispatch(display); - wl_display_roundtrip(display); - - if (!dwl_ipc_manager) - die("bad dwl-ipc protocol"); - - wl_display_roundtrip(display); - - if (mode == WATCH) - while (wl_display_dispatch(display) != -1) - ; - - return 0; -} + free(line); + fclose(stream); + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/src/action/client.h b/src/action/client.h index ce71a5c6..43eb2c7b 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -57,4 +57,7 @@ void client_tile_resize(Client *c, struct wlr_box geo, int32_t interact) { if (!c->isfullscreen && !c->ismaximizescreen) { resize(c, geo, interact); } -} \ No newline at end of file +} + +static uint32_t next_client_id = 0; +uint32_t generate_client_id(void) { return ++next_client_id; } \ No newline at end of file diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 6229ea19..b1d30758 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -946,6 +946,8 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value, } else if (strcmp(func_name, "focusdir") == 0) { func = focusdir; (*arg).i = parse_direction(arg_value); + } else if (strcmp(func_name, "focusid") == 0) { + func = focusid; } else if (strcmp(func_name, "incnmaster") == 0) { func = incnmaster; (*arg).i = atoi(arg_value); @@ -2356,6 +2358,7 @@ bool parse_option(Config *config, char *key, char *value) { binding->arg.v = NULL; binding->arg.v2 = NULL; binding->arg.v3 = NULL; + binding->arg.tc = NULL; binding->func = parse_func_name(func_name, &binding->arg, arg_value, arg_value2, arg_value3, arg_value4, arg_value5); @@ -2437,6 +2440,7 @@ bool parse_option(Config *config, char *key, char *value) { binding->arg.v = NULL; binding->arg.v2 = NULL; binding->arg.v3 = NULL; + binding->arg.tc = NULL; // TODO: remove this in next version if (binding->mod == 0 && @@ -2522,6 +2526,7 @@ bool parse_option(Config *config, char *key, char *value) { binding->arg.v = NULL; binding->arg.v2 = NULL; binding->arg.v3 = NULL; + binding->arg.tc = NULL; binding->func = parse_func_name(func_name, &binding->arg, arg_value, arg_value2, arg_value3, arg_value4, arg_value5); @@ -2672,6 +2677,7 @@ bool parse_option(Config *config, char *key, char *value) { binding->arg.v = NULL; binding->arg.v2 = NULL; binding->arg.v3 = NULL; + binding->arg.tc = NULL; binding->func = parse_func_name(func_name, &binding->arg, arg_value, arg_value2, arg_value3, arg_value4, arg_value5); diff --git a/src/dispatch/bind_declare.h b/src/dispatch/bind_declare.h index b79ad689..a04ba0c1 100644 --- a/src/dispatch/bind_declare.h +++ b/src/dispatch/bind_declare.h @@ -73,4 +73,5 @@ int32_t scroller_stack(const Arg *arg); int32_t toggle_all_floating(const Arg *arg); int32_t dwindle_toggle_split_direction(const Arg *arg); int32_t dwindle_split_horizontal(const Arg *arg); -int32_t dwindle_split_vertical(const Arg *arg); \ No newline at end of file +int32_t dwindle_split_vertical(const Arg *arg); +int32_t focusid(const Arg *arg); \ No newline at end of file diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 84fd7ffc..281a9676 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -34,10 +34,8 @@ int32_t bind_to_view(const Arg *arg) { int32_t chvt(const Arg *arg) { struct timespec ts; - // prevent the animation to rquest the new frame allow_frame_scheduling = false; - // backup current tag and monitor name if (selmon) { chvt_backup_tag = selmon->pertag->curtag; strncpy(chvt_backup_selmon, selmon->wlr_output->name, @@ -46,19 +44,15 @@ int32_t chvt(const Arg *arg) { wlr_session_change_vt(session, arg->ui); - // wait for DRM device to stabilize and ensure the session state is inactive ts.tv_sec = 0; - ts.tv_nsec = 100000000; // 200ms + ts.tv_nsec = 100000000; nanosleep(&ts, NULL); - // allow frame scheduling, - // because session state is now inactive, rendermon will not enter allow_frame_scheduling = true; return 1; } int32_t create_virtual_output(const Arg *arg) { - if (!wlr_backend_is_multi(backend)) { wlr_log(WLR_ERROR, "Expected a multi backend"); return 0; @@ -78,7 +72,6 @@ int32_t create_virtual_output(const Arg *arg) { } int32_t destroy_all_virtual_output(const Arg *arg) { - if (!wlr_backend_is_multi(backend)) { wlr_log(WLR_ERROR, "Expected a multi backend"); return 0; @@ -87,8 +80,6 @@ int32_t destroy_all_virtual_output(const Arg *arg) { Monitor *m, *tmp; wl_list_for_each_safe(m, tmp, &mons, link) { if (wlr_output_is_headless(m->wlr_output)) { - // if(selmon == m) - // selmon = NULL; wlr_output_destroy(m->wlr_output); wlr_log(WLR_INFO, "Virtual output destroyed"); } @@ -104,7 +95,7 @@ int32_t defaultgaps(const Arg *arg) { int32_t exchange_client(const Arg *arg) { if (!selmon) return 0; - Client *c = selmon->sel; + Client *c = arg->tc ? arg->tc : selmon->sel; if (!c || c->isfloating) return 0; @@ -112,7 +103,7 @@ int32_t exchange_client(const Arg *arg) { return 0; Client *tc = direction_select(arg); - tc = get_focused_stack_client(tc); + tc = get_focused_stack_client(tc, arg->tc); if (!tc) return 0; @@ -125,7 +116,7 @@ int32_t exchange_stack_client(const Arg *arg) { if (!selmon) return 0; - Client *c = selmon->sel; + Client *c = arg->tc ? arg->tc : selmon->sel; Client *tc = NULL; if (!c || c->isfloating || c->isfullscreen || c->ismaximizescreen) return 0; @@ -142,7 +133,7 @@ int32_t exchange_stack_client(const Arg *arg) { int32_t focusdir(const Arg *arg) { Client *c = NULL; c = direction_select(arg); - c = get_focused_stack_client(c); + c = get_focused_stack_client(c, arg->tc); if (c) { focusclient(c, 1); if (config.warpcursor) @@ -161,7 +152,6 @@ int32_t focusdir(const Arg *arg) { } int32_t focuslast(const Arg *arg) { - Client *c = NULL; Client *tc = NULL; bool begin = false; @@ -233,7 +223,7 @@ int32_t focusmon(const Arg *arg) { if (config.warpcursor) { warp_cursor_to_selmon(selmon); } - c = focustop(selmon); + c = arg->tc ? arg->tc : focustop(selmon); if (!c) { selmon->sel = NULL; wlr_seat_pointer_notify_clear_focus(seat); @@ -246,8 +236,7 @@ int32_t focusmon(const Arg *arg) { } int32_t focusstack(const Arg *arg) { - /* Focus the next or previous client (in tiling order) on selmon */ - Client *sel = focustop(selmon); + Client *sel = arg->tc ? arg->tc : focustop(selmon); Client *tc = NULL; if (!sel) @@ -257,7 +246,6 @@ int32_t focusstack(const Arg *arg) { } else { tc = get_next_stack_client(sel, true); } - /* If only one client is visible on selmon, then c == sel */ if (!tc) return 0; @@ -356,10 +344,7 @@ int32_t setmfact(const Arg *arg) { } int32_t killclient(const Arg *arg) { - Client *c = NULL; - if (!selmon) - return 0; - c = selmon->sel; + Client *c = arg->tc ? arg->tc : (selmon ? selmon->sel : NULL); if (c) { pending_kill_client(c); } @@ -378,7 +363,6 @@ int32_t moveresize(const Arg *arg) { grabc = NULL; return 0; } - /* Float the window and tell motionnotify to grab it */ if (grabc->isfloating == 0 && arg->ui == CurMove) { grabc->drag_to_tile = true; exit_scroller_stack(grabc); @@ -399,20 +383,16 @@ int32_t moveresize(const Arg *arg) { switch (cursor_mode = arg->ui) { case CurMove: - grabcx = cursor->x - grabc->geom.x; grabcy = cursor->y - grabc->geom.y; wlr_cursor_set_xcursor(cursor, cursor_mgr, "grab"); break; case CurResize: - /* Doesn't work for X11 output - the next absolute motion event - * returns the cursor to where it started */ if (grabc->isfloating) { rzcorner = config.drag_corner; grabcx = (int)round(cursor->x); grabcy = (int)round(cursor->y); if (rzcorner == 4) - /* identify the closest corner index */ rzcorner = (grabcx - grabc->geom.x < grabc->geom.x + grabc->geom.width - grabcx ? 0 @@ -440,14 +420,11 @@ int32_t moveresize(const Arg *arg) { } int32_t movewin(const Arg *arg) { - Client *c = NULL; - if (!selmon) - return 0; - c = selmon->sel; + Client *c = arg->tc ? arg->tc : (selmon ? selmon->sel : NULL); if (!c || c->isfullscreen) return 0; if (!c->isfloating) - togglefloating(NULL); + setfloating(c, 1); switch (arg->ui) { case NUM_TYPE_MINUS: @@ -485,10 +462,7 @@ int32_t quit(const Arg *arg) { } int32_t resizewin(const Arg *arg) { - Client *c = NULL; - if (!selmon) - return 0; - c = selmon->sel; + Client *c = arg->tc ? arg->tc : (selmon ? selmon->sel : NULL); int32_t offsetx = 0, offsety = 0; if (!c || c->isfullscreen || c->ismaximizescreen) @@ -559,18 +533,17 @@ int32_t resizewin(const Arg *arg) { } int32_t restore_minimized(const Arg *arg) { - Client *c = NULL; + Client *c = arg->tc ? arg->tc : (selmon ? selmon->sel : NULL); if (selmon && selmon->isoverview) return 0; - if (selmon && selmon->sel && selmon->sel->is_in_scratchpad && - selmon->sel->is_scratchpad_show) { - client_pending_minimized_state(selmon->sel, 0); - selmon->sel->is_scratchpad_show = 0; - selmon->sel->is_in_scratchpad = 0; - selmon->sel->isnamedscratchpad = 0; - setborder_color(selmon->sel); + if (c && c->is_in_scratchpad && c->is_scratchpad_show) { + client_pending_minimized_state(c, 0); + c->is_scratchpad_show = 0; + c->is_in_scratchpad = 0; + c->isnamedscratchpad = 0; + setborder_color(c); return 0; } @@ -629,11 +602,10 @@ int32_t set_proportion(const Arg *arg) { !config.scroller_ignore_proportion_single) return 0; - Client *tc = selmon->sel; + Client *tc = arg->tc ? arg->tc : selmon->sel; if (!tc) return 0; - /* 获取堆叠头部客户端 */ tc = scroll_get_stack_head_client(tc); if (!tc) return 0; @@ -646,12 +618,10 @@ int32_t set_proportion(const Arg *arg) { if (st) node = find_scroller_node(st, tc); - /* 同时更新节点和客户端字段 */ if (node) node->scroller_proportion = arg->f; tc->scroller_proportion = arg->f; - /* 可选的即时几何更新,arrange 时会重新计算 */ uint32_t max_client_width = m->w.width - 2 * config.scroller_structs - config.gappih; tc->geom.width = max_client_width * arg->f; @@ -675,7 +645,7 @@ int32_t switch_proportion_preset(const Arg *arg) { !config.scroller_ignore_proportion_single) return 0; - Client *tc = selmon->sel; + Client *tc = arg->tc ? arg->tc : selmon->sel; if (!tc) return 0; @@ -691,11 +661,9 @@ int32_t switch_proportion_preset(const Arg *arg) { if (st) node = find_scroller_node(st, tc); - /* 优先从节点读取当前比例,以确保切换基于正确的值 */ float current_proportion = node ? node->scroller_proportion : tc->scroller_proportion; - /* 查找预设目标 */ for (int32_t i = 0; i < config.scroller_proportion_preset_count; i++) { if (config.scroller_proportion_preset[i] == current_proportion) { if (arg->i == NEXT) { @@ -720,7 +688,6 @@ int32_t switch_proportion_preset(const Arg *arg) { if (target_proportion == 0.0f) target_proportion = config.scroller_proportion_preset[0]; - /* 更新节点和客户端 */ if (node) node->scroller_proportion = target_proportion; tc->scroller_proportion = target_proportion; @@ -739,11 +706,11 @@ int32_t smartmovewin(const Arg *arg) { int32_t buttom, top, left, right, tar; if (!selmon) return 0; - c = selmon->sel; + c = arg->tc ? arg->tc : selmon->sel; if (!c || c->isfullscreen) return 0; if (!c->isfloating) - setfloating(selmon->sel, true); + setfloating(c, true); nx = c->geom.x; ny = c->geom.y; @@ -842,7 +809,7 @@ int32_t smartresizewin(const Arg *arg) { int32_t buttom, top, left, right, tar; if (!selmon) return 0; - c = selmon->sel; + c = arg->tc ? arg->tc : selmon->sel; if (!c || c->isfullscreen) return 0; if (!c->isfloating) @@ -909,10 +876,7 @@ int32_t smartresizewin(const Arg *arg) { } int32_t centerwin(const Arg *arg) { - Client *c = NULL; - if (!selmon) - return 0; - c = selmon->sel; + Client *c = arg->tc ? arg->tc : (selmon ? selmon->sel : NULL); if (!c || c->isfullscreen || c->ismaximizescreen) return 0; @@ -945,7 +909,6 @@ int32_t spawn_shell(const Arg *arg) { return 0; if (fork() == 0) { - // 1. 忽略可能导致 coredump 的信号 signal(SIGSEGV, SIG_IGN); signal(SIGABRT, SIG_IGN); signal(SIGILL, SIG_IGN); @@ -954,11 +917,8 @@ int32_t spawn_shell(const Arg *arg) { setsid(); execlp("sh", "sh", "-c", arg->v, (char *)NULL); - - // fallback to bash execlp("bash", "bash", "-c", arg->v, (char *)NULL); - // if execlp fails, we should not reach here wlr_log(WLR_DEBUG, "mango: failed to execute command '%s' with shell: %s\n", arg->v, strerror(errno)); @@ -972,7 +932,6 @@ int32_t spawn(const Arg *arg) { return 0; if (fork() == 0) { - // 1. 忽略可能导致 coredump 的信号 signal(SIGSEGV, SIG_IGN); signal(SIGABRT, SIG_IGN); signal(SIGILL, SIG_IGN); @@ -980,20 +939,17 @@ int32_t spawn(const Arg *arg) { dup2(STDERR_FILENO, STDOUT_FILENO); setsid(); - // 2. 对整个参数字符串进行单词展开 wordexp_t p; if (wordexp(arg->v, &p, 0) != 0) { wlr_log(WLR_DEBUG, "mango: wordexp failed for '%s'\n", arg->v); _exit(EXIT_FAILURE); } - // 3. 执行命令(p.we_wordv 已经是 argv 数组) execvp(p.we_wordv[0], p.we_wordv); - // 4. execvp 失败时:打印错误,释放 wordexp 资源,然后退出 wlr_log(WLR_DEBUG, "mango: execvp '%s' failed: %s\n", p.we_wordv[0], strerror(errno)); - wordfree(&p); // 释放 wordexp 分配的内存 + wordfree(&p); _exit(EXIT_FAILURE); } return 0; @@ -1031,7 +987,6 @@ int32_t switch_keyboard_layout(const Arg *arg) { return 0; } - // 1. 获取当前布局和计算下一个布局 xkb_layout_index_t current = xkb_state_serialize_layout( keyboard->xkb_state, XKB_STATE_LAYOUT_EFFECTIVE); const int32_t num_layouts = xkb_keymap_num_layouts(keyboard->keymap); @@ -1047,14 +1002,12 @@ int32_t switch_keyboard_layout(const Arg *arg) { next = (current + 1) % num_layouts; } - // 6. 应用新 keymap uint32_t depressed = keyboard->modifiers.depressed; uint32_t latched = keyboard->modifiers.latched; uint32_t locked = keyboard->modifiers.locked; wlr_keyboard_notify_modifiers(keyboard, depressed, latched, locked, next); - // 7. 更新 seat wlr_seat_set_keyboard(seat, keyboard); wlr_seat_keyboard_notify_modifiers(seat, &keyboard->modifiers); @@ -1067,7 +1020,6 @@ int32_t switch_keyboard_layout(const Arg *arg) { struct wlr_keyboard *tkb = (struct wlr_keyboard *)id->device_data; wlr_keyboard_notify_modifiers(tkb, depressed, latched, locked, next); - // 7. 更新 seat wlr_seat_set_keyboard(seat, tkb); wlr_seat_keyboard_notify_modifiers(seat, &tkb->modifiers); } @@ -1077,7 +1029,6 @@ int32_t switch_keyboard_layout(const Arg *arg) { } int32_t switch_layout(const Arg *arg) { - int32_t jk, ji; char *target_layout_name = NULL; uint32_t len; @@ -1110,13 +1061,8 @@ int32_t switch_layout(const Arg *arg) { len = MAX(strlen(layouts[ji].name), strlen(target_layout_name)); if (strncmp(layouts[ji].name, target_layout_name, len) == 0) { selmon->pertag->ltidxs[target_tag] = &layouts[ji]; - - break; } } - clear_fullscreen_and_maximized_state(selmon); - arrange(selmon, false, false); - printstatus(); return 0; } @@ -1137,7 +1083,7 @@ int32_t switch_layout(const Arg *arg) { int32_t tag(const Arg *arg) { if (!selmon) return 0; - Client *target_client = selmon->sel; + Client *target_client = arg->tc ? arg->tc : selmon->sel; tag_client(arg, target_client); return 0; } @@ -1146,7 +1092,7 @@ int32_t tagmon(const Arg *arg) { Monitor *m = NULL, *cm = NULL, *oldmon = NULL; if (!selmon) return 0; - Client *c = focustop(selmon); + Client *c = arg->tc ? arg->tc : focustop(selmon); if (!c) return 0; @@ -1180,8 +1126,8 @@ int32_t tagmon(const Arg *arg) { return 0; } - if (c == selmon->sel) { - selmon->sel = NULL; + if (c == oldmon->sel) { + oldmon->sel = NULL; } setmon(c, m, newtags, true); @@ -1196,8 +1142,6 @@ int32_t tagmon(const Arg *arg) { selmon = c->mon; c->float_geom = setclient_coordinate_center(c, c->mon, c->float_geom, 0, 0); - // 重新计算居中的坐标 - // 重新计算居中的坐标 if (c->isfloating) { c->geom = c->float_geom; target = get_tags_first_tag(c->tags); @@ -1219,12 +1163,11 @@ int32_t tagmon(const Arg *arg) { int32_t tagsilent(const Arg *arg) { Client *fc = NULL; - Client *target_client = NULL; + Client *target_client = arg->tc ? arg->tc : (selmon ? selmon->sel : NULL); - if (!selmon || !selmon->sel) + if (!target_client) return 0; - target_client = selmon->sel; target_client->tags = arg->ui & TAGMASK; wl_list_for_each(fc, &clients, link) { if (fc && fc != target_client && target_client->tags & fc->tags && @@ -1241,7 +1184,8 @@ int32_t tagtoleft(const Arg *arg) { if (!selmon) return 0; - if (selmon->sel != NULL && + Client *sel = arg->tc ? arg->tc : selmon->sel; + if (sel != NULL && __builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1) { uint32_t target = selmon->tagset[selmon->seltags] >> 1; @@ -1252,7 +1196,8 @@ int32_t tagtoleft(const Arg *arg) { selmon->carousel_anim_dir = -1; } - tag(&(Arg){.ui = target & TAGMASK, .i = arg->i}); + Arg a = {.ui = target & TAGMASK, .i = arg->i, .tc = sel}; + tag(&a); selmon->carousel_anim_dir = 0; } return 0; @@ -1262,7 +1207,8 @@ int32_t tagtoright(const Arg *arg) { if (!selmon) return 0; - if (selmon->sel != NULL && + Client *sel = arg->tc ? arg->tc : selmon->sel; + if (sel != NULL && __builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1) { uint32_t target = selmon->tagset[selmon->seltags] << 1; @@ -1273,7 +1219,8 @@ int32_t tagtoright(const Arg *arg) { selmon->carousel_anim_dir = 1; } - tag(&(Arg){.ui = target & TAGMASK, .i = arg->i}); + Arg a = {.ui = target & TAGMASK, .i = arg->i, .tc = sel}; + tag(&a); selmon->carousel_anim_dir = 0; } return 0; @@ -1296,7 +1243,6 @@ int32_t toggle_named_scratchpad(const Arg *arg) { } target_client->isnamedscratchpad = 1; - apply_named_scratchpad(target_client); return 0; } @@ -1342,7 +1288,7 @@ int32_t toggle_scratchpad(const Arg *arg) { int32_t togglefakefullscreen(const Arg *arg) { if (!selmon) return 0; - Client *sel = focustop(selmon); + Client *sel = arg->tc ? arg->tc : focustop(selmon); if (sel) setfakefullscreen(sel, !sel->isfakefullscreen); return 0; @@ -1352,7 +1298,7 @@ int32_t togglefloating(const Arg *arg) { if (!selmon) return 0; - Client *sel = focustop(selmon); + Client *sel = arg->tc ? arg->tc : focustop(selmon); if (selmon && selmon->isoverview) return 0; @@ -1376,7 +1322,7 @@ int32_t togglefullscreen(const Arg *arg) { if (!selmon) return 0; - Client *sel = focustop(selmon); + Client *sel = arg->tc ? arg->tc : focustop(selmon); if (!sel) return 0; @@ -1395,15 +1341,17 @@ int32_t toggleglobal(const Arg *arg) { if (!selmon) return 0; - if (!selmon->sel) + Client *c = arg->tc ? arg->tc : selmon->sel; + if (!c) return 0; - if (selmon->sel->is_in_scratchpad) { - selmon->sel->is_in_scratchpad = 0; - selmon->sel->is_scratchpad_show = 0; - selmon->sel->isnamedscratchpad = 0; + + if (c->is_in_scratchpad) { + c->is_in_scratchpad = 0; + c->is_scratchpad_show = 0; + c->isnamedscratchpad = 0; } - selmon->sel->isglobal ^= 1; - setborder_color(selmon->sel); + c->isglobal ^= 1; + setborder_color(c); return 0; } @@ -1420,7 +1368,7 @@ int32_t togglemaximizescreen(const Arg *arg) { if (!selmon) return 0; - Client *sel = focustop(selmon); + Client *sel = arg->tc ? arg->tc : focustop(selmon); if (!sel) return 0; @@ -1441,23 +1389,23 @@ int32_t toggleoverlay(const Arg *arg) { if (!selmon) return 0; - if (!selmon->sel || !selmon->sel->mon || selmon->sel->isfullscreen) { + Client *c = arg->tc ? arg->tc : selmon->sel; + if (!c || !c->mon || c->isfullscreen) { return 0; } - selmon->sel->isoverlay ^= 1; + c->isoverlay ^= 1; - if (selmon->sel->isoverlay) { - wlr_scene_node_reparent(&selmon->sel->scene->node, layers[LyrOverlay]); - wlr_scene_node_raise_to_top(&selmon->sel->scene->node); - } else if (client_should_overtop(selmon->sel) && selmon->sel->isfloating) { - wlr_scene_node_reparent(&selmon->sel->scene->node, layers[LyrTop]); + if (c->isoverlay) { + wlr_scene_node_reparent(&c->scene->node, layers[LyrOverlay]); + wlr_scene_node_raise_to_top(&c->scene->node); + } else if (client_should_overtop(c) && c->isfloating) { + wlr_scene_node_reparent(&c->scene->node, layers[LyrTop]); } else { - wlr_scene_node_reparent( - &selmon->sel->scene->node, - layers[selmon->sel->isfloating ? LyrTop : LyrTile]); + wlr_scene_node_reparent(&c->scene->node, + layers[c->isfloating ? LyrTop : LyrTile]); } - setborder_color(selmon->sel); + setborder_color(c); return 0; } @@ -1466,7 +1414,7 @@ int32_t toggletag(const Arg *arg) { return 0; uint32_t newtags; - Client *sel = focustop(selmon); + Client *sel = arg->tc ? arg->tc : focustop(selmon); if (!sel) return 0; @@ -1649,15 +1597,20 @@ int32_t viewcrossmon(const Arg *arg) { } int32_t tagcrossmon(const Arg *arg) { - if (!selmon || !selmon->sel) + if (!selmon) + return 0; + + Client *c = arg->tc ? arg->tc : selmon->sel; + if (!c) return 0; if (match_monitor_spec(arg->v, selmon)) { - tag_client(arg, selmon->sel); + tag_client(arg, c); return 0; } - tagmon(&(Arg){.ui = arg->ui, .i = UNDIR, .v = arg->v}); + Arg a = {.ui = arg->ui, .i = UNDIR, .v = arg->v, .tc = c}; + tagmon(&a); return 0; } @@ -1681,15 +1634,13 @@ int32_t comboview(const Arg *arg) { } int32_t zoom(const Arg *arg) { - Client *c = NULL, *sel = focustop(selmon); + Client *c = NULL, *sel = arg->tc ? arg->tc : focustop(selmon); if (!sel || !selmon || !selmon->pertag->ltidxs[selmon->pertag->curtag]->arrange || sel->isfloating) return 0; - /* Search for the first tiled window that is not sel, marking sel as - * NULL if we pass it along the way */ wl_list_for_each(c, &clients, link) if (VISIBLEON(c, selmon) && !c->isfloating) { if (c != sel) @@ -1697,12 +1648,9 @@ int32_t zoom(const Arg *arg) { sel = NULL; } - /* Return if no other tiled window was found */ if (&c->link == &clients) return 0; - /* If we passed sel, move c to the front; otherwise, move sel to the - * front */ if (!sel) sel = c; wl_list_remove(&sel->link); @@ -1727,8 +1675,9 @@ int32_t minimized(const Arg *arg) { if (selmon && selmon->isoverview) return 0; - if (selmon->sel && !selmon->sel->isminimized) { - set_minimized(selmon->sel); + Client *c = arg->tc ? arg->tc : selmon->sel; + if (c && !c->isminimized) { + set_minimized(c); } return 0; } @@ -1748,8 +1697,9 @@ int32_t toggleoverview(const Arg *arg) { if (!selmon) return 0; - if (selmon->isoverview && config.ov_tab_mode && arg->i != 1 && - selmon->sel) { + Client *sel = arg->tc ? arg->tc : selmon->sel; + + if (selmon->isoverview && config.ov_tab_mode && arg->i != 1 && sel) { focusstack(&(Arg){.i = 1}); return 0; } @@ -1774,9 +1724,9 @@ int32_t toggleoverview(const Arg *arg) { selmon->isoverview ^= 1; return 0; } - } else if (!selmon->isoverview && selmon->sel) { - target = get_tags_first_tag(selmon->sel->tags); - } else if (!selmon->isoverview && !selmon->sel) { + } else if (!selmon->isoverview && sel) { + target = get_tags_first_tag(sel->tags); + } else if (!selmon->isoverview && !sel) { target = (1 << (selmon->pertag->prevtag - 1)); view(&(Arg){.ui = target}, false); fix_mon_tagset_from_overview(selmon); @@ -1784,11 +1734,7 @@ int32_t toggleoverview(const Arg *arg) { return 0; } - // 正常视图到overview,退出所有窗口的浮动和全屏状态参与平铺, - // overview到正常视图,还原之前退出的浮动和全屏窗口状态 if (selmon->isoverview) { - - // 让游戏窗口无法强制约束鼠标 wlr_seat_pointer_clear_focus(seat); wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); @@ -1879,7 +1825,6 @@ int32_t scroller_apply_stack(Client *c, Client *target_client, struct TagScrollerState *st = ensure_scroller_state(m, tag); - /* 获取当前节点 */ struct ScrollerStackNode *cnode = find_scroller_node(st, c); if (!cnode) @@ -1888,20 +1833,16 @@ int32_t scroller_apply_stack(Client *c, Client *target_client, struct ScrollerStackNode *tnode = target_client ? find_scroller_node(st, target_client) : NULL; - /* 若方向为 UNDIR 且有目标,直接插入到目标尾部 */ if (direction == UNDIR && target_client && target_client->mon == c->mon) { scroller_insert_stack(c, target_client, false); return 0; } - /* 处理从堆叠中移出的情况(方向 LEFT/UP 或 RIGHT/DOWN) */ if (cnode->prev_in_stack || cnode->next_in_stack) { struct ScrollerStackNode *move_out_refer_node = cnode->prev_in_stack ? cnode->prev_in_stack : cnode->next_in_stack; scroller_node_remove(st, cnode); - // 必须先更新,不然里面节点还存着的是cnode的信息, - // 会造成stach_head/stack_tail指向的客户端不对 update_scroller_state(c->mon); Client *stack_head = @@ -1928,12 +1869,10 @@ int32_t scroller_apply_stack(Client *c, Client *target_client, if (!tnode || target_client->mon != c->mon) return 0; - /* 找到目标堆叠的尾部节点 */ struct ScrollerStackNode *tail = tnode; while (tail->next_in_stack) tail = tail->next_in_stack; - /* 通过封装好的插入函数实现(尾部插入) */ scroller_insert_stack(c, tail->client, false); if (c != tail->client) { @@ -1946,7 +1885,7 @@ int32_t scroller_apply_stack(Client *c, Client *target_client, int32_t scroller_stack(const Arg *arg) { if (!selmon) return 0; - Client *c = selmon->sel; + Client *c = arg->tc ? arg->tc : selmon->sel; if (!c || !c->mon || c->isfloating || !is_scroller_layout(selmon)) return 0; @@ -1956,15 +1895,18 @@ int32_t scroller_stack(const Arg *arg) { } int32_t toggle_all_floating(const Arg *arg) { - if (!selmon || !selmon->sel) + if (!selmon) return 0; - Client *c = NULL; - bool should_floating = !selmon->sel->isfloating; + Client *ref = arg->tc ? arg->tc : selmon->sel; + if (!ref) + return 0; + bool should_floating = !ref->isfloating; + + Client *c; wl_list_for_each(c, &clients, link) { if (VISIBLEON(c, selmon)) { - if (c->isfloating && !should_floating) { c->old_master_inner_per = 0.0f; c->old_stack_inner_per = 0.0f; @@ -1980,7 +1922,6 @@ int32_t toggle_all_floating(const Arg *arg) { } int32_t dwindle_set_split_direction(Client *c, bool istoggle, bool horizontal) { - const Layout *layout = c->mon->pertag->ltidxs[c->mon->pertag->curtag]; if (layout->id != DWINDLE) @@ -2005,31 +1946,40 @@ int32_t dwindle_set_split_direction(Client *c, bool istoggle, bool horizontal) { } int32_t dwindle_toggle_split_direction(const Arg *arg) { - if (!selmon || !selmon->sel) + if (!selmon) return 0; - Client *c = selmon->sel; + Client *c = arg->tc ? arg->tc : selmon->sel; if (!c || !c->mon || c->isfloating) return 0; - return dwindle_set_split_direction(selmon->sel, true, false); + return dwindle_set_split_direction(c, true, false); } int32_t dwindle_split_horizontal(const Arg *arg) { - if (!selmon || !selmon->sel) + if (!selmon) return 0; - Client *c = selmon->sel; + Client *c = arg->tc ? arg->tc : selmon->sel; if (!c || !c->mon || c->isfloating) return 0; - return dwindle_set_split_direction(selmon->sel, false, true); + return dwindle_set_split_direction(c, false, true); } int32_t dwindle_split_vertical(const Arg *arg) { - if (!selmon || !selmon->sel) + if (!selmon) return 0; - Client *c = selmon->sel; + Client *c = arg->tc ? arg->tc : selmon->sel; if (!c || !c->mon || c->isfloating) return 0; - return dwindle_set_split_direction(selmon->sel, false, false); + return dwindle_set_split_direction(c, false, false); +} + +int32_t focusid(const Arg *arg) { + if (!selmon || !arg->tc) + return 0; + + Client *c = arg->tc; + focusclient(c, 1); + return 0; } \ No newline at end of file diff --git a/src/ext-protocol/dwl-ipc.h b/src/ext-protocol/dwl-ipc.h index ab0bdb8d..1d57a20e 100644 --- a/src/ext-protocol/dwl-ipc.h +++ b/src/ext-protocol/dwl-ipc.h @@ -192,7 +192,7 @@ void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) { if (wl_resource_get_version(ipc_output->resource) >= ZDWL_IPC_OUTPUT_V2_LAST_LAYER_SINCE_VERSION) { zdwl_ipc_output_v2_send_last_layer(ipc_output->resource, - monitor->last_surface_ws_name); + monitor->last_open_surface); } if (wl_resource_get_version(ipc_output->resource) >= diff --git a/src/fetch/client.h b/src/fetch/client.h index e39e39b3..85b296a3 100644 --- a/src/fetch/client.h +++ b/src/fetch/client.h @@ -452,7 +452,7 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating, Client *direction_select(const Arg *arg) { - Client *tc = selmon->sel; + Client *tc = arg->tc ? arg->tc : selmon->sel; if (!tc) return NULL; @@ -593,12 +593,12 @@ bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc) { return false; } -Client *get_focused_stack_client(Client *sc) { +Client *get_focused_stack_client(Client *sc, Client *custom_focus_client) { if (!sc || sc->isfloating) return sc; Client *tc = NULL; - Client *fc = focustop(sc->mon); + Client *fc = custom_focus_client ? custom_focus_client : focustop(sc->mon); if (fc->isfloating || sc->isfloating) return sc; @@ -616,4 +616,4 @@ Client *get_focused_stack_client(Client *sc) { } } return sc; -} +} \ No newline at end of file diff --git a/src/ipc/ipc.h b/src/ipc/ipc.h new file mode 100644 index 00000000..21ead90d --- /dev/null +++ b/src/ipc/ipc.h @@ -0,0 +1,970 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum ipc_watch_type { + IPC_WATCH_NONE, + IPC_WATCH_MONITOR, + IPC_WATCH_CLIENT, + IPC_WATCH_TAGS, + IPC_WATCH_ALL_MONITORS, + IPC_WATCH_ALL_TAGS, + IPC_WATCH_ALL_CLIENTS, + IPC_WATCH_KEYMODE, + IPC_WATCH_KB_LAYOUT, + IPC_WATCH_LAST_OPEN_SURFACE, +}; + +struct ipc_watch_client { + struct wl_list link; + int fd; + struct wl_event_source *source; + enum ipc_watch_type type; + union { + struct { + char name[64]; + } monitor; + struct { + uint32_t id; + } client; + struct { + char mon_name[64]; + } tags; + } target; +}; + +static struct wl_list watch_clients; + +struct ipc_client_state { + int fd; + struct wl_event_source *source; + struct wl_event_loop *loop; + char *buf; + size_t buf_len; + size_t buf_cap; +}; + +static void ipc_remove_watch_client(struct ipc_watch_client *wc); +static void ipc_notify_json_to_fd(int fd, cJSON *json); + +/* ---------- 工具函数 ---------- */ + +static Monitor *monitor_by_name(const char *name) { + Monitor *m; + wl_list_for_each(m, &mons, link) { + if (strcmp(m->wlr_output->name, name) == 0) + return m; + } + return NULL; +} + +static Client *client_by_id(uint32_t id) { + Client *c; + wl_list_for_each(c, &clients, link) { + if (c->id == id) + return c; + } + return NULL; +} + +static const char *ipc_get_layout_str(void) { + struct wlr_keyboard *keyboard = &kb_group->wlr_group->keyboard; + xkb_layout_index_t current = xkb_state_serialize_layout( + keyboard->xkb_state, XKB_STATE_LAYOUT_EFFECTIVE); + static char layout[32]; + get_layout_abbr(layout, + xkb_keymap_layout_get_name(keyboard->keymap, current)); + return layout; +} + +static cJSON *tags_mask_to_array(uint32_t tagmask) { + cJSON *arr = cJSON_CreateArray(); + for (int i = 0; i < LENGTH(tags); i++) + if (tagmask & (1 << i)) + cJSON_AddItemToArray(arr, cJSON_CreateNumber(i + 1)); + return arr; +} + +static cJSON *build_tags_json(Monitor *m) { + cJSON *tags_array = cJSON_CreateArray(); + Client *c = NULL; + for (int tag = 1; tag <= LENGTH(tags); tag++) { + int numclients = 0; + bool is_active = false, is_urgent = false; + uint32_t tagmask = 1 << (tag - 1); + if (tagmask & m->tagset[m->seltags]) + is_active = true; + wl_list_for_each(c, &clients, link) { + if (c->mon != m) + continue; + if (!(c->tags & tagmask & TAGMASK)) + continue; + if (c->isurgent) + is_urgent = true; + numclients++; + } + cJSON *tag_obj = cJSON_CreateObject(); + cJSON_AddNumberToObject(tag_obj, "index", tag); + cJSON_AddBoolToObject(tag_obj, "is_active", is_active); + cJSON_AddBoolToObject(tag_obj, "is_urgent", is_urgent); + cJSON_AddStringToObject(tag_obj, "layout", + m->pertag->ltidxs[tag]->symbol); + cJSON_AddNumberToObject(tag_obj, "client_count", numclients); + cJSON_AddItemToArray(tags_array, tag_obj); + } + return tags_array; +} + +static cJSON *monitor_active_client(Monitor *m) { + cJSON *obj = cJSON_CreateObject(); + if (!m->sel) { + cJSON_AddNullToObject(obj, "id"); + cJSON_AddNullToObject(obj, "title"); + cJSON_AddNullToObject(obj, "appid"); + return obj; + } + Client *c = m->sel; + cJSON_AddNumberToObject(obj, "id", c->id); + cJSON_AddStringToObject(obj, "title", client_get_title(c)); + cJSON_AddStringToObject(obj, "appid", client_get_appid(c)); + return obj; +} + +static cJSON *monitor_active_tags(Monitor *m) { + cJSON *arr = cJSON_CreateArray(); + uint32_t tagset; + if (m->isoverview) { + cJSON_AddItemToArray(arr, cJSON_CreateNumber(0)); + return arr; + } + tagset = m->tagset[m->seltags]; + for (int i = 0; i < LENGTH(tags); i++) + if (tagset & (1 << i)) + cJSON_AddItemToArray(arr, cJSON_CreateNumber(i + 1)); + return arr; +} + +static cJSON *build_client_json(Client *c) { + cJSON *obj = cJSON_CreateObject(); + cJSON_AddNumberToObject(obj, "id", c->id); + cJSON_AddNumberToObject(obj, "pid", c->pid); + cJSON_AddStringToObject(obj, "title", client_get_title(c)); + cJSON_AddStringToObject(obj, "appid", client_get_appid(c)); + cJSON_AddStringToObject(obj, "monitor", c->mon->wlr_output->name); + cJSON_AddItemToObject(obj, "tags", tags_mask_to_array(c->tags)); + cJSON_AddBoolToObject(obj, "is_focused", c == focustop(c->mon)); + cJSON_AddBoolToObject(obj, "is_fullscreen", c->isfullscreen); + cJSON_AddBoolToObject(obj, "is_floating", c->isfloating); + cJSON_AddBoolToObject(obj, "is_maximized", c->ismaximizescreen); + cJSON_AddBoolToObject(obj, "is_global", c->isglobal); + cJSON_AddBoolToObject(obj, "is_unglobal", c->isunglobal); + cJSON_AddBoolToObject(obj, "is_overlay", c->isoverlay); + cJSON_AddBoolToObject(obj, "is_fakefullscreen", c->isfakefullscreen); + cJSON_AddBoolToObject(obj, "is_minimized", c->isminimized); + cJSON_AddBoolToObject(obj, "is_urgent", c->isurgent); + cJSON_AddBoolToObject(obj, "is_scratchpad", c->is_in_scratchpad); + cJSON_AddBoolToObject(obj, "is_namedscratchpad", c->isnamedscratchpad); + cJSON_AddNumberToObject(obj, "x", c->geom.x); + cJSON_AddNumberToObject(obj, "y", c->geom.y); + cJSON_AddNumberToObject(obj, "width", c->geom.width); + cJSON_AddNumberToObject(obj, "height", c->geom.height); + return obj; +} + +static cJSON *build_monitor_json(Monitor *m) { + cJSON *resp = cJSON_CreateObject(); + cJSON_AddStringToObject(resp, "name", m->wlr_output->name); + cJSON_AddBoolToObject(resp, "active", m == selmon); + cJSON_AddNumberToObject(resp, "x", m->m.x); + cJSON_AddNumberToObject(resp, "y", m->m.y); + cJSON_AddNumberToObject(resp, "width", m->m.width); + cJSON_AddNumberToObject(resp, "height", m->m.height); + cJSON_AddNumberToObject(resp, "scale", m->wlr_output->scale); + cJSON_AddNumberToObject(resp, "layout_index", + m->pertag->ltidxs[m->pertag->curtag] - layouts); + cJSON_AddStringToObject(resp, "layout_symbol", + m->pertag->ltidxs[m->pertag->curtag]->symbol); + cJSON_AddStringToObject(resp, "last_open_surface", m->last_open_surface); + cJSON_AddItemToObject(resp, "tags", build_tags_json(m)); + cJSON_AddItemToObject(resp, "active_tags", monitor_active_tags(m)); + cJSON_AddItemToObject(resp, "active_client", monitor_active_client(m)); + cJSON_AddItemToObject(resp, "keymode", cJSON_CreateString(keymode.mode)); + cJSON_AddItemToObject(resp, "keyboardlayout", + cJSON_CreateString(ipc_get_layout_str())); + return resp; +} + +static cJSON *build_all_tags_entry(Monitor *m) { + cJSON *entry = cJSON_CreateObject(); + cJSON_AddStringToObject(entry, "monitor", m->wlr_output->name); + cJSON_AddItemToObject(entry, "tags", build_tags_json(m)); + return entry; +} + +static cJSON *build_all_tags_response(void) { + cJSON *arr = cJSON_CreateArray(); + Monitor *m; + wl_list_for_each(m, &mons, link) + cJSON_AddItemToArray(arr, build_all_tags_entry(m)); + cJSON *resp = cJSON_CreateObject(); + cJSON_AddItemToObject(resp, "all_tags", arr); + return resp; +} + +static cJSON *build_monitor_tags_response(Monitor *m) { + cJSON *resp = cJSON_CreateObject(); + cJSON_AddStringToObject(resp, "monitor", m->wlr_output->name); + cJSON_AddItemToObject(resp, "tags", build_tags_json(m)); + cJSON_AddItemToObject(resp, "active_tags", monitor_active_tags(m)); + return resp; +} + +static void send_static_json(int fd, const char *json_str) { + size_t len = strlen(json_str); + send(fd, json_str, len, MSG_NOSIGNAL); +} + +/* ---------- 一次性命令处理 ---------- */ +static void handle_command(int client_fd, const char *cmd_raw) { + cJSON *resp = NULL; + char *json_str = NULL; + char cmd[1024]; + + strncpy(cmd, cmd_raw, sizeof(cmd) - 1); + cmd[sizeof(cmd) - 1] = '\0'; + for (char *p = cmd; *p; p++) + if (*p == ',') + *p = ' '; + + if (strcmp(cmd, "get version") == 0) { + resp = cJSON_CreateObject(); + cJSON_AddStringToObject(resp, "version", VERSION); + } else if (strcmp(cmd, "get keymode") == 0) { + resp = cJSON_CreateObject(); + cJSON_AddStringToObject(resp, "keymode", keymode.mode); + } else if (strcmp(cmd, "get keyboardlayout") == 0) { + resp = cJSON_CreateObject(); + cJSON_AddStringToObject(resp, "layout", ipc_get_layout_str()); + } else if (strncmp(cmd, "get last_open_surface ", 25) == 0) { + const char *name = cmd + 25; + Monitor *m = monitor_by_name(name); + if (!m) { + send_static_json(client_fd, "{\"error\":\"monitor not found\"}\n"); + return; + } + resp = cJSON_CreateObject(); + cJSON_AddStringToObject(resp, "monitor", m->wlr_output->name); + cJSON_AddStringToObject(resp, "last_open_surface", + m->last_open_surface); + } else if (strncmp(cmd, "get monitor ", 12) == 0) { + Monitor *m = monitor_by_name(cmd + 12); + if (!m) { + send_static_json(client_fd, "{\"error\":\"monitor not found\"}\n"); + return; + } + resp = build_monitor_json(m); + } else if (strncmp(cmd, "get client ", 11) == 0) { + Client *c = client_by_id((uint32_t)atoi(cmd + 11)); + if (!c) { + send_static_json(client_fd, "{\"error\":\"client not found\"}\n"); + return; + } + resp = build_client_json(c); + } else if (strncmp(cmd, "get tag ", 8) == 0) { + char mon_name[64]; + int ext_tag_idx; + if (sscanf(cmd + 8, "%63s %d", mon_name, &ext_tag_idx) != 2) { + send_static_json( + client_fd, + "{\"error\":\"usage: get tag \"}\n"); + return; + } + int tag_idx = ext_tag_idx - 1; + Monitor *m = monitor_by_name(mon_name); + if (!m || tag_idx < 0 || tag_idx >= LENGTH(tags)) { + send_static_json(client_fd, + "{\"error\":\"invalid monitor or tag index\"}\n"); + return; + } + uint32_t tagmask = 1 << tag_idx; + int numclients = 0, focused_client = 0; + bool is_active = false, is_urgent = false; + if (tagmask & m->tagset[m->seltags]) + is_active = true; + + Client *c, *focused = focustop(m); + wl_list_for_each(c, &clients, link) { + if (c->mon != m || !(c->tags & tagmask)) + continue; + if (c == focused) + focused_client = 1; + if (c->isurgent) + is_urgent = true; + numclients++; + } + resp = cJSON_CreateObject(); + cJSON_AddStringToObject(resp, "monitor", m->wlr_output->name); + cJSON_AddNumberToObject(resp, "tag_index", ext_tag_idx); + cJSON_AddBoolToObject(resp, "is_active", is_active); + cJSON_AddBoolToObject(resp, "is_urgent", is_urgent); + cJSON_AddNumberToObject(resp, "client_count", numclients); + cJSON_AddBoolToObject(resp, "focused_client", focused_client); + } else if (strcmp(cmd, "get all-clients") == 0) { + cJSON *arr = cJSON_CreateArray(); + Client *c; + wl_list_for_each(c, &clients, link) + cJSON_AddItemToArray(arr, build_client_json(c)); + resp = cJSON_CreateObject(); + cJSON_AddItemToObject(resp, "clients", arr); + } else if (strcmp(cmd, "get all-monitors") == 0) { + cJSON *arr = cJSON_CreateArray(); + Monitor *m; + wl_list_for_each(m, &mons, link) + cJSON_AddItemToArray(arr, build_monitor_json(m)); + resp = cJSON_CreateObject(); + cJSON_AddItemToObject(resp, "monitors", arr); + } else if (strcmp(cmd, "get all-tags") == 0) { + resp = build_all_tags_response(); + } else if (strncmp(cmd, "get tags ", 9) == 0) { + Monitor *m = monitor_by_name(cmd + 9); + if (!m) { + send_static_json(client_fd, "{\"error\":\"monitor not found\"}\n"); + return; + } + resp = build_monitor_tags_response(m); + } else if (strncmp(cmd, "dispatch ", 9) == 0) { + char *dispatch_copy = strdup(cmd_raw + 9); + char *out = dispatch_copy, *ptr = dispatch_copy; + int client_id = -1; + while (*ptr) { + while (*ptr == ' ' || *ptr == '\t') + *out++ = *ptr++; + if (strncmp(ptr, "client,", 7) == 0) { + char *end; + long id = strtol(ptr + 7, &end, 10); + if (id > 0 && end > ptr + 7 && (*end == '\0' || *end == ',')) { + client_id = (int)id; + ptr = end; + if (*ptr == ',') + ptr++; + continue; + } + } + *out++ = *ptr++; + } + *out = '\0'; + + char *tokens[6] = {NULL}; + int token_count = 0; + char *saveptr; + char *token = strtok_r(dispatch_copy, ",", &saveptr); + while (token && token_count < 6) { + while (*token == ' ' || *token == '\t') + token++; + char *end = token + strlen(token) - 1; + while (end >= token && (*end == ' ' || *end == '\t')) + *end-- = '\0'; + tokens[token_count++] = token; + token = strtok_r(NULL, ",", &saveptr); + } + + Arg arg = {0}; + int32_t (*func)(const Arg *) = + parse_func_name(token_count > 0 ? tokens[0] : "", &arg, + token_count > 1 ? tokens[1] : NULL, + token_count > 2 ? tokens[2] : NULL, + token_count > 3 ? tokens[3] : NULL, + token_count > 4 ? tokens[4] : NULL, + token_count > 5 ? tokens[5] : NULL); + + if (func && client_id > 0) + arg.tc = client_by_id((uint32_t)client_id); + + if (func) { + func(&arg); + send_static_json(client_fd, "{\"success\":true}\n"); + } else { + send_static_json(client_fd, "{\"error\":\"unknown function\"}\n"); + } + + if (arg.v) + free(arg.v); + if (arg.v2) + free(arg.v2); + if (arg.v3) + free(arg.v3); + free(dispatch_copy); + return; // Fast path exit + } else { + send_static_json(client_fd, "{\"error\":\"unknown command\"}\n"); + return; + } + + if (resp) { + json_str = cJSON_PrintUnformatted(resp); + if (json_str) { + size_t len = strlen(json_str); + char *msg = malloc(len + 2); + if (msg) { + snprintf(msg, len + 2, "%s\n", json_str); + send(client_fd, msg, len + 1, MSG_NOSIGNAL); + free(msg); + } + free(json_str); + } + cJSON_Delete(resp); + } +} + +/* ---------- Watch 模式支持 ---------- */ +static void ipc_notify_json_to_fd(int fd, cJSON *json) { + char *str = cJSON_PrintUnformatted(json); + if (!str) + return; + size_t len = strlen(str); + char *msg = malloc(len + 2); + if (!msg) { + free(str); + return; + } + snprintf(msg, len + 2, "%s\n", str); + if (send(fd, msg, len + 1, MSG_NOSIGNAL) < 0) { + struct ipc_watch_client *wc, *tmp; + wl_list_for_each_safe(wc, tmp, &watch_clients, link) { + if (wc->fd == fd) { + ipc_remove_watch_client(wc); + break; + } + } + } + free(msg); + free(str); +} + +static void ipc_remove_watch_client(struct ipc_watch_client *wc) { + wl_list_remove(&wc->link); + wl_event_source_remove(wc->source); + close(wc->fd); + free(wc); +} + +static int ipc_watch_data_handler(int fd, uint32_t mask, void *data) { + struct ipc_watch_client *wc = data; + if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) { + ipc_remove_watch_client(wc); + return 0; + } + if (mask & WL_EVENT_READABLE) { + char buf[64]; + ssize_t n = recv(fd, buf, sizeof(buf), MSG_DONTWAIT); + if (n == 0 || (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK)) { + ipc_remove_watch_client(wc); + } + } + return 0; +} + +static bool handle_watch_command(int fd, const char *cmd, + struct ipc_client_state *client) { + enum ipc_watch_type type = IPC_WATCH_NONE; + const char *arg = NULL; + uint32_t client_id = 0; + + if (strncmp(cmd, "watch monitor ", 14) == 0) { + type = IPC_WATCH_MONITOR; + arg = cmd + 14; + } else if (strncmp(cmd, "watch client ", 13) == 0) { + type = IPC_WATCH_CLIENT; + client_id = (uint32_t)atoi(cmd + 13); + } else if (strncmp(cmd, "watch tags ", 11) == 0) { + type = IPC_WATCH_TAGS; + arg = cmd + 11; + } else if (strcmp(cmd, "watch all-monitors") == 0) { + type = IPC_WATCH_ALL_MONITORS; + } else if (strcmp(cmd, "watch all-tags") == 0) { + type = IPC_WATCH_ALL_TAGS; + } else if (strcmp(cmd, "watch all-clients") == 0) { + type = IPC_WATCH_ALL_CLIENTS; + } else if (strcmp(cmd, "watch keymode") == 0) { + type = IPC_WATCH_KEYMODE; + } else if (strcmp(cmd, "watch keyboardlayout") == 0) { + type = IPC_WATCH_KB_LAYOUT; + } else if (strncmp(cmd, "watch last_open_surface ", 27) == 0) { + type = IPC_WATCH_LAST_OPEN_SURFACE; + arg = cmd + 27; + } + + if (type == IPC_WATCH_NONE) + return false; + + struct ipc_watch_client *wc = calloc(1, sizeof(*wc)); + wc->fd = fd; + wc->type = type; + + if ((type == IPC_WATCH_MONITOR || type == IPC_WATCH_LAST_OPEN_SURFACE) && + arg) + snprintf(wc->target.monitor.name, sizeof(wc->target.monitor.name), "%s", + arg); + else if (type == IPC_WATCH_TAGS && arg) + snprintf(wc->target.tags.mon_name, sizeof(wc->target.tags.mon_name), + "%s", arg); + else if (type == IPC_WATCH_CLIENT) + wc->target.client.id = client_id; + + wl_event_source_remove(client->source); + wc->source = wl_event_loop_add_fd( + client->loop, fd, WL_EVENT_READABLE | WL_EVENT_HANGUP | WL_EVENT_ERROR, + ipc_watch_data_handler, wc); + wl_list_insert(&watch_clients, &wc->link); + + /* 推送初始状态 */ + cJSON *json = NULL; + switch (type) { + case IPC_WATCH_MONITOR: { + Monitor *m = monitor_by_name(arg); + if (m) + json = build_monitor_json(m); + break; + } + case IPC_WATCH_LAST_OPEN_SURFACE: { + Monitor *m = monitor_by_name(arg); + if (m) { + json = cJSON_CreateObject(); + cJSON_AddStringToObject(json, "monitor", m->wlr_output->name); + cJSON_AddStringToObject(json, "last_open_surface", + m->last_open_surface); + } + break; + } + case IPC_WATCH_CLIENT: { + Client *c = client_by_id(client_id); + if (c) + json = build_client_json(c); + break; + } + case IPC_WATCH_TAGS: { + Monitor *m = monitor_by_name(arg); + if (m) + json = build_monitor_tags_response(m); + break; + } + case IPC_WATCH_ALL_MONITORS: { + cJSON *arr = cJSON_CreateArray(); + Monitor *m; + wl_list_for_each(m, &mons, link) + cJSON_AddItemToArray(arr, build_monitor_json(m)); + json = cJSON_CreateObject(); + cJSON_AddItemToObject(json, "monitors", arr); + break; + } + case IPC_WATCH_ALL_TAGS: { + json = build_all_tags_response(); + break; + } + case IPC_WATCH_ALL_CLIENTS: { + cJSON *arr = cJSON_CreateArray(); + Client *c; + wl_list_for_each(c, &clients, link) + cJSON_AddItemToArray(arr, build_client_json(c)); + json = cJSON_CreateObject(); + cJSON_AddItemToObject(json, "clients", arr); + break; + } + case IPC_WATCH_KEYMODE: { + json = cJSON_CreateObject(); + cJSON_AddStringToObject(json, "keymode", keymode.mode); + break; + } + case IPC_WATCH_KB_LAYOUT: { + json = cJSON_CreateObject(); + cJSON_AddStringToObject(json, "layout", ipc_get_layout_str()); + break; + } + default: + break; + } + + if (json) { + ipc_notify_json_to_fd(fd, json); + cJSON_Delete(json); + } + + free(client->buf); + free(client); + return true; +} + +/* ---------- Socket 事件处理 ---------- */ +static int ipc_handle_client_data(int fd, uint32_t mask, void *data) { + struct ipc_client_state *client = data; + if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) + goto cleanup; + + if (mask & WL_EVENT_READABLE) { + size_t available = client->buf_cap - client->buf_len; + if (available < 4096) { + size_t new_cap = client->buf_cap ? client->buf_cap * 2 : 8192; + char *new_buf = realloc(client->buf, new_cap); + if (!new_buf) { + wlr_log(WLR_ERROR, "IPC: out of memory"); + goto cleanup; + } + client->buf = new_buf; + client->buf_cap = new_cap; + available = client->buf_cap - client->buf_len; + } + + // 直接读取到 client->buf 尾部,跳过临时数组 + ssize_t n = recv(fd, client->buf + client->buf_len, available - 1, + MSG_DONTWAIT); + if (n <= 0) + goto cleanup; + + client->buf_len += n; + client->buf[client->buf_len] = '\0'; + + char *nl = memchr(client->buf, '\n', client->buf_len); + if (!nl) { + if (client->buf_len > 1024 * 1024) + goto cleanup; // 防御过长命令 + return 0; + } + *nl = '\0'; + char *cmd = client->buf; + + bool is_watch = handle_watch_command(fd, cmd, client); + if (is_watch) + return 0; + + handle_command(fd, cmd); + goto cleanup; + } + return 0; + +cleanup: + close(client->fd); + wl_event_source_remove(client->source); + free(client->buf); + free(client); + return 0; +} + +static int ipc_handle_connection(int fd, uint32_t mask, void *data) { + struct wl_event_loop *loop = data; + int client_fd = accept(fd, NULL, NULL); + if (client_fd < 0) + return 0; + + int flags = fcntl(client_fd, F_GETFL, 0); + fcntl(client_fd, F_SETFL, flags | O_NONBLOCK); + + struct ipc_client_state *client = calloc(1, sizeof(*client)); + client->fd = client_fd; + client->loop = loop; + client->source = wl_event_loop_add_fd( + loop, client_fd, WL_EVENT_READABLE | WL_EVENT_HANGUP | WL_EVENT_ERROR, + ipc_handle_client_data, client); + return 0; +} + +/* ---------- 外部通知接口 ---------- */ + +void ipc_notify_monitor(Monitor *m) { + 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_MONITOR && + strcmp(m->wlr_output->name, wc->target.monitor.name) == 0) { + if (!json_str) { + cJSON *json = build_monitor_json(m); + 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); + } + } + if (json_str) + free(json_str); +} + +void ipc_notify_last_surface_ws_name(Monitor *m) { + 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_LAST_OPEN_SURFACE && + strcmp(m->wlr_output->name, wc->target.monitor.name) == 0) { + if (!json_str) { + cJSON *json = cJSON_CreateObject(); + cJSON_AddStringToObject(json, "monitor", m->wlr_output->name); + cJSON_AddStringToObject(json, "last_open_surface", + m->last_open_surface); + 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); + } + } + if (json_str) + free(json_str); +} + +void ipc_notify_client(Client *c) { + 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_CLIENT && c->id == wc->target.client.id) { + if (!json_str) { + cJSON *json = build_client_json(c); + 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); + } + } + if (json_str) + free(json_str); +} + +void ipc_notify_tags(Monitor *m) { + 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_TAGS && + strcmp(m->wlr_output->name, wc->target.tags.mon_name) == 0) { + if (!json_str) { + cJSON *json = build_monitor_tags_response(m); + 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); + } + } + if (json_str) + free(json_str); +} + +void ipc_notify_all_monitors(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_ALL_MONITORS) { + if (!json_str) { + cJSON *arr = cJSON_CreateArray(); + Monitor *m; + wl_list_for_each(m, &mons, link) + cJSON_AddItemToArray(arr, build_monitor_json(m)); + cJSON *json = cJSON_CreateObject(); + cJSON_AddItemToObject(json, "monitors", arr); + 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); + } + } + if (json_str) + free(json_str); +} + +void ipc_notify_all_clients(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_ALL_CLIENTS) { + if (!json_str) { + cJSON *arr = cJSON_CreateArray(); + Client *c; + wl_list_for_each(c, &clients, link) + cJSON_AddItemToArray(arr, build_client_json(c)); + cJSON *json = cJSON_CreateObject(); + cJSON_AddItemToObject(json, "clients", arr); + 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); + } + } + if (json_str) + free(json_str); +} + +void ipc_notify_all_tags(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_ALL_TAGS) { + if (!json_str) { + cJSON *json = build_all_tags_response(); + 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); + } + } + if (json_str) + free(json_str); +} + +void ipc_notify_keymode(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_KEYMODE) { + if (!json_str) { + cJSON *json = cJSON_CreateObject(); + cJSON_AddStringToObject(json, "keymode", keymode.mode); + 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); + } + } + if (json_str) + free(json_str); +} + +void ipc_notify_kb_layout(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_KB_LAYOUT) { + if (!json_str) { + cJSON *json = cJSON_CreateObject(); + cJSON_AddStringToObject(json, "layout", ipc_get_layout_str()); + 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); + } + } + if (json_str) + free(json_str); +} + +/* ---------- 初始化与清理 ---------- */ +static int ipc_sock_fd = -1; +static struct wl_event_source *ipc_event_source = NULL; +static char ipc_socket_path[256]; + +void ipc_init(struct wl_event_loop *event_loop) { + wl_list_init(&watch_clients); + + const char *xdg_runtime = getenv("XDG_RUNTIME_DIR"); + if (!xdg_runtime) + return; + + snprintf(ipc_socket_path, sizeof(ipc_socket_path), "%s/mango-%d.sock", + xdg_runtime, getpid()); + + ipc_sock_fd = + socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); + if (ipc_sock_fd < 0) + return; + + struct sockaddr_un addr = {.sun_family = AF_UNIX}; + strncpy(addr.sun_path, ipc_socket_path, sizeof(addr.sun_path) - 1); + + unlink(ipc_socket_path); + if (bind(ipc_sock_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + close(ipc_sock_fd); + return; + } + listen(ipc_sock_fd, 16); + + setenv("MANGO_INSTANCE_SIGNATURE", ipc_socket_path, 1); + + ipc_event_source = + wl_event_loop_add_fd(event_loop, ipc_sock_fd, WL_EVENT_READABLE, + ipc_handle_connection, event_loop); +} + +void ipc_cleanup(void) { + if (ipc_event_source) + wl_event_source_remove(ipc_event_source); + if (ipc_sock_fd >= 0) + close(ipc_sock_fd); + unlink(ipc_socket_path); + unsetenv("MANGO_INSTANCE_SIGNATURE"); + + 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 diff --git a/src/mango.c b/src/mango.c index 055e2561..1aaaf471 100644 --- a/src/mango.c +++ b/src/mango.c @@ -194,28 +194,6 @@ enum seat_config_shortcuts_inhibit { SHORTCUTS_INHIBIT_ENABLE, }; -// 事件掩码枚举 -enum print_event_type { - PRINT_ACTIVE = 1 << 0, - PRINT_TAG = 1 << 1, - PRINT_LAYOUT = 1 << 2, - PRINT_TITLE = 1 << 3, - PRINT_APPID = 1 << 4, - PRINT_LAYOUT_SYMBOL = 1 << 5, - PRINT_FULLSCREEN = 1 << 6, - PRINT_FLOATING = 1 << 7, - PRINT_X = 1 << 8, - PRINT_Y = 1 << 9, - PRINT_WIDTH = 1 << 10, - PRINT_HEIGHT = 1 << 11, - PRINT_LAST_LAYER = 1 << 12, - PRINT_KB_LAYOUT = 1 << 13, - PRINT_KEYMODE = 1 << 14, - PRINT_SCALEFACTOR = 1 << 15, - PRINT_FRAME = 1 << 16, - PRINT_ALL = (1 << 17) - 1 // 所有位都设为1 -}; - typedef struct Pertag Pertag; typedef struct Monitor Monitor; typedef struct Client Client; @@ -238,6 +216,7 @@ typedef struct { char *v3; uint32_t ui; uint32_t ui2; + Client *tc; } Arg; typedef struct { @@ -442,6 +421,7 @@ struct Client { float old_grid_row_per; int32_t grid_col_idx; int32_t grid_row_idx; + uint32_t id; }; typedef struct { @@ -554,7 +534,7 @@ struct Monitor { uint32_t visible_tiling_clients; uint32_t visible_scroll_tiling_clients; uint32_t visible_fake_tiling_clients; - char last_surface_ws_name[256]; + char last_open_surface[256]; struct wlr_ext_workspace_group_handle_v1 *ext_group; bool iscleanuping; int8_t carousel_anim_dir; @@ -867,7 +847,8 @@ static Client *find_client_by_direction(Client *tc, const Arg *arg, static void exit_scroller_stack(Client *c); static Client *scroll_get_stack_head_client(Client *c); static bool client_only_in_one_tag(Client *c); -static Client *get_focused_stack_client(Client *sc); +static Client *get_focused_stack_client(Client *sc, + Client *custom_focus_client); static bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc); static void monitor_stop_skip_frame_timer(Monitor *m); static int monitor_skip_frame_timeout_callback(void *data); @@ -1024,6 +1005,7 @@ static char *env_vars[] = {"DISPLAY", "XDG_SESSION_TYPE", "XCURSOR_THEME", "XCURSOR_SIZE", + "MANGO_INSTANCE_SIGNATURE", NULL}; static struct { enum wp_cursor_shape_device_v1_shape shape; @@ -1115,6 +1097,7 @@ static struct wl_event_source *sync_keymap; #include "dispatch/bind_define.h" #include "ext-protocol/all.h" #include "fetch/fetch.h" +#include "ipc/ipc.h" #include "layout/arrange.h" #include "layout/dwindle.h" #include "layout/horizontal.h" @@ -2531,6 +2514,7 @@ void cleanuplisteners(void) { } void cleanup(void) { + ipc_cleanup(); cleanuplisteners(); #ifdef XWAYLAND wlr_xwayland_destroy(xwayland); @@ -2656,9 +2640,9 @@ void maplayersurfacenotify(struct wl_listener *listener, void *data) { if (!l->mon) return; - strncpy(l->mon->last_surface_ws_name, layer_surface->namespace, - sizeof(l->mon->last_surface_ws_name) - 1); // 最多拷贝255个字符 - l->mon->last_surface_ws_name[sizeof(l->mon->last_surface_ws_name) - 1] = + strncpy(l->mon->last_open_surface, layer_surface->namespace, + sizeof(l->mon->last_open_surface) - 1); // 最多拷贝255个字符 + l->mon->last_open_surface[sizeof(l->mon->last_open_surface) - 1] = '\0'; // 确保字符串以null结尾 // 初始化几何位置 @@ -4341,6 +4325,8 @@ mapnotify(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, map); int32_t i = 0; + c->id = generate_client_id(); + /* Create scene tree for this client and its border */ c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LyrTile]); wlr_scene_node_set_enabled(&c->scene->node, c->type != XDGShell); @@ -5722,13 +5708,30 @@ void create_output(struct wlr_backend *backend, void *data) { // 修改信号处理函数,接收掩码参数 void handle_print_status(struct wl_listener *listener, void *data) { + ipc_notify_keymode(); + ipc_notify_kb_layout(); + ipc_notify_all_tags(); + ipc_notify_all_clients(); + ipc_notify_all_monitors(); + + Client *c = NULL; + wl_list_for_each(c, &clients, link) { + if (c->iskilling) + continue; + ipc_notify_client(c); + } + Monitor *m = NULL; wl_list_for_each(m, &mons, link) { if (!m->wlr_output->enabled) { continue; } - dwl_ext_workspace_printstatus(m); + ipc_notify_monitor(m); + ipc_notify_tags(m); + ipc_notify_last_surface_ws_name(m); + + dwl_ext_workspace_printstatus(m); dwl_ipc_output_printstatus(m); } } @@ -5757,6 +5760,9 @@ void setup(void) { * clients from the Unix socket, manging Wayland globals, and so on. */ dpy = wl_display_create(); event_loop = wl_display_get_event_loop(dpy); + + ipc_init(event_loop); + tablet_mgr = wlr_tablet_v2_create(dpy); /* The backend is a wlroots feature which abstracts the underlying input * and output hardware. The autocreate option will choose the most