smsg.c
516 lines
| 12.0 KiB
| text/x-c
|
CLexer
/ smsg / smsg.c
| r0 | #define _GNU_SOURCE | |||
| #include "arg.h" | ||||
| #include <poll.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <sys/socket.h> | ||||
| #include <wayland-client.h> | ||||
| #include <wayland-util.h> | ||||
| #include "dwl-ipc-unstable-v2-protocol.h" | ||||
| #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 int Oflag; | ||||
| static int Tflag; | ||||
| static int Lflag; | ||||
| static int Pflag; | ||||
| static int Fflag; | ||||
| static int oflag; | ||||
| static int tflag; | ||||
| static int lflag; | ||||
| static int cflag; | ||||
| static int vflag; | ||||
| static int mflag; | ||||
| static int fflag; | ||||
| static uint32_t occ, seltags, sel, urg; | ||||
| static char *output_name; | ||||
| static char *focused_output; | ||||
| static uint32_t tagcount; | ||||
| static char *tagset; | ||||
| static char *layout_name; | ||||
| static size_t layoutcount, layout_idx; | ||||
| static char *client_tags; | ||||
| struct output { | ||||
| char *output_name; | ||||
| uint32_t name; | ||||
| }; | ||||
| static size_t outputs_buflen = 4; | ||||
| static struct output *outputs; | ||||
| static size_t outputcount; | ||||
| static struct wl_display *display; | ||||
| static struct wl_registry *registry; | ||||
| static struct zdwl_ipc_manager_v2 *dwl_ipc_manager; | ||||
| 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 *oname = data; | ||||
| if (oname) printf("%s ", oname); | ||||
| printf("toggle\n"); | ||||
| } | ||||
| static void | ||||
| dwl_ipc_output_active(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, | ||||
| uint32_t active) | ||||
| { | ||||
| if (active && (!output_name || !*output_name)) | ||||
| output_name = strdup(data); | ||||
| if (active) { | ||||
| free(focused_output); | ||||
| focused_output = data ? strdup(data) : NULL; | ||||
| } | ||||
| if (!oflag) return; | ||||
| char *oname = data; | ||||
| if (oname) printf("%s ", oname); | ||||
| printf("selmon %u\n", !!active); | ||||
| } | ||||
| 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_NONE) seltags |= 1<<tag; | ||||
| if (state == ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE) urg |= 1<<tag; | ||||
| if (clients > 0) occ |= 1<<tag; | ||||
| if (!(mode & GET)) return; | ||||
| if (data) printf("%s ", (char *)data); | ||||
| printf("tag %u %u %u %u\n", tag, 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; | ||||
| if (data) printf("%s ", (char *)data); | ||||
| 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; | ||||
| if (data) printf("%s ", (char *)data); | ||||
| 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; | ||||
| if (data) printf("%s ", (char *)data); | ||||
| printf("appid %s\n", appid); | ||||
| } | ||||
| static void | ||||
| dwl_ipc_output_fullscreen(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, | ||||
| uint32_t is_fullscreen) | ||||
| { | ||||
| if (!mflag) return; | ||||
| if (data) printf("%s ", (char *)data); | ||||
| 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; | ||||
| if (data) printf("%s ", (char *)data); | ||||
| 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) == 0)) return; | ||||
| if (lflag) { | ||||
| if (layout_idx == 0) { | ||||
| for (char *c = layout_name; *c; c++) { | ||||
| if (*c < '0' || *c > '9') | ||||
| die("bad layout %s", layout_name); | ||||
| layout_idx = *c-'0' + layout_idx*10; | ||||
| } | ||||
| } | ||||
| if (layout_idx >= layoutcount) | ||||
| die("bad layout %s", layout_name); | ||||
| zdwl_ipc_output_v2_set_layout(dwl_ipc_output, layout_idx); | ||||
| } | ||||
| if (tflag) { | ||||
| uint32_t mask = seltags; | ||||
| char *t = tagset; | ||||
| uint32_t i = 0; | ||||
| int toggle = 1; | ||||
| if (*t == '!') { | ||||
| toggle = 0; | ||||
| t++; | ||||
| } | ||||
| for (; *t && *t >= '0' && *t <= '9'; t++) | ||||
| i = *t-'0' + i*10; | ||||
| if (toggle && !*t) mask = 1<<i; | ||||
| for (; *t; t++, i++) { | ||||
| switch (*t) { | ||||
| case '-': | ||||
| mask &= ~(1<<i); | ||||
| break; | ||||
| case '+': | ||||
| mask |= 1<<i; | ||||
| break; | ||||
| case '^': | ||||
| mask ^= 1<<i; | ||||
| break; | ||||
| } | ||||
| } | ||||
| if (i > tagcount) die("bad tagset %s", tagset); | ||||
| zdwl_ipc_output_v2_set_tags(dwl_ipc_output, mask, toggle); | ||||
| } | ||||
| if (cflag) { | ||||
| uint32_t and = ~0, xor = 0; | ||||
| char *t = client_tags; | ||||
| uint32_t i = 0; | ||||
| for (; *t && *t >= '0' && *t <= '9'; t++) | ||||
| i = *t-'0' + i*10; | ||||
| if (!*t) { | ||||
| and = 0; | ||||
| xor = 1<<i; | ||||
| } | ||||
| for (; *t; t++, i++) { | ||||
| switch (*t) { | ||||
| case '-': | ||||
| and &= ~(1<<i); | ||||
| break; | ||||
| case '+': | ||||
| and &= ~(1<<i); | ||||
| xor |= 1<<i; | ||||
| break; | ||||
| case '^': | ||||
| xor |= 1<<i; | ||||
| break; | ||||
| } | ||||
| } | ||||
| if (i > tagcount) die("bad client tagset %s", client_tags); | ||||
| zdwl_ipc_output_v2_set_client_tags(dwl_ipc_output, and, xor); | ||||
| } | ||||
| wl_display_flush(display); | ||||
| exit(0); | ||||
| } else { | ||||
| if (Fflag && mode == WATCH && focused_output) | ||||
| printf("focusmon %s\n", focused_output); | ||||
| if (tflag) { | ||||
| if (data) printf("%s ", (char *)data); | ||||
| printf("tags %u %u %u %u\n", occ, seltags, sel, urg); | ||||
| occ = seltags = sel = urg = 0; | ||||
| } | ||||
| } | ||||
| 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, | ||||
| .frame = dwl_ipc_output_frame, | ||||
| }; | ||||
| static void | ||||
| wl_output_name(void *data, struct wl_output *output, const char *name) | ||||
| { | ||||
| if (outputs) { | ||||
| struct output *o = (struct output *)data; | ||||
| o->output_name = strdup(name); | ||||
| printf("+ "); | ||||
| } | ||||
| if (Oflag) printf("%s\n", name); | ||||
| if (output_name && *output_name && strcmp(output_name, name) != 0) { | ||||
| wl_output_release(output); | ||||
| return; | ||||
| } | ||||
| if (dwl_ipc_manager) { | ||||
| 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 && *output_name ? NULL : strdup(name)); | ||||
| } | ||||
| } | ||||
| static void | ||||
| wl_output_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, int32_t w, int32_t h, int32_t subpixel, const char *make, const char *model, int32_t transform) { | ||||
| } | ||||
| static void | ||||
| wl_output_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) | ||||
| { | ||||
| } | ||||
| static void | ||||
| wl_output_done(void *data, struct wl_output *wl_output) | ||||
| { | ||||
| } | ||||
| static void | ||||
| wl_output_scale(void *data, struct wl_output *wl_output, int32_t factor) | ||||
| { | ||||
| } | ||||
| static void | ||||
| wl_output_description(void *data, struct wl_output *output, const char *description) | ||||
| { | ||||
| } | ||||
| static const struct wl_output_listener output_listener = { | ||||
| .geometry = wl_output_geometry, | ||||
| .mode = wl_output_mode, | ||||
| .done = wl_output_done, | ||||
| .scale = wl_output_scale, | ||||
| .name = wl_output_name, | ||||
| .description = wl_output_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); | ||||
| wl_output_add_listener(o, &output_listener, outputs ? &outputs[outputcount] : NULL); | ||||
| if (!outputs) return; | ||||
| if (outputcount >= outputs_buflen) { | ||||
| outputs_buflen *= 2; | ||||
| outputs = reallocarray(outputs, outputs_buflen, sizeof(struct output)); | ||||
| } | ||||
| outputs[outputcount].name = name; | ||||
| outputcount++; | ||||
| } 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, version < 2 ? version : 2); | ||||
| zdwl_ipc_manager_v2_add_listener(dwl_ipc_manager, &dwl_ipc_listener, NULL); | ||||
| } | ||||
| } | ||||
| static void | ||||
| global_remove(void *data, struct wl_registry *wl_registry, uint32_t name) | ||||
| { | ||||
| if (!outputs) return; | ||||
| for (size_t i = 0; i < outputcount; i++) { | ||||
| if (outputs[i].name == name) { | ||||
| printf("- %s\n", outputs[i].output_name); | ||||
| free(outputs[i].output_name); | ||||
| outputs[i] = outputs[--outputcount]; | ||||
| } | ||||
| } | ||||
| } | ||||
| static const struct wl_registry_listener registry_listener = { | ||||
| .global = global_add, | ||||
| .global_remove = global_remove, | ||||
| }; | ||||
| static void | ||||
| usage(int code) | ||||
| { | ||||
| fprintf(code ? stderr : stdout, | ||||
| "usage:" | ||||
| "\t%1$s [-OTLPF]\n" | ||||
| "\t%1$s [-o <output>] -s [-t <tags>] [-l <layout>] [-c <tags>]\n" | ||||
| "\t%1$s [-o <output>] (-g | -w) [-FOotlcvmf]\n", | ||||
| argv0); | ||||
| exit(code); | ||||
| } | ||||
| int | ||||
| main(int argc, char *argv[]) | ||||
| { | ||||
| ARGBEGIN { | ||||
| case '?': case 'h': | ||||
| usage(0); break; | ||||
| case 's': | ||||
| if (mode != NONE) usage(1); | ||||
| mode = SET; | ||||
| break; | ||||
| case 'g': | ||||
| if (mode != NONE) usage(1); | ||||
| mode = GET; | ||||
| break; | ||||
| case 'w': | ||||
| if (mode != NONE) usage(1); | ||||
| mode = WATCH; | ||||
| break; | ||||
| case 'o': | ||||
| if (mode & SET) | ||||
| output_name = EARGF(usage(1)); | ||||
| else if (mode & GET || !(output_name = ARGF())) | ||||
| oflag = 1; | ||||
| break; | ||||
| case 't': | ||||
| tflag = 1; | ||||
| if (mode & GET) break; | ||||
| mode = SET; | ||||
| tagset = EARGF(usage(1)); | ||||
| break; | ||||
| case 'l': | ||||
| lflag = 1; | ||||
| if (mode & GET) break; | ||||
| mode = SET; | ||||
| layout_name = EARGF(usage(1)); | ||||
| break; | ||||
| case 'c': | ||||
| cflag = 1; | ||||
| if (mode & GET) break; | ||||
| mode = SET; | ||||
| client_tags = EARGF(usage(1)); | ||||
| break; | ||||
| case 'O': | ||||
| Oflag = 1; | ||||
| if (mode & SET) usage(1); | ||||
| if (mode & WATCH) | ||||
| outputs = malloc(outputs_buflen * sizeof(struct output)); | ||||
| else | ||||
| mode = GET; | ||||
| break; | ||||
| case 'T': | ||||
| Tflag = 1; | ||||
| if (mode && mode != GET) usage(1); | ||||
| mode = GET; | ||||
| break; | ||||
| case 'L': | ||||
| Lflag = 1; | ||||
| if (mode && mode != GET) usage(1); | ||||
| mode = GET; | ||||
| break; | ||||
| case 'P': | ||||
| Pflag = 1; | ||||
| if (mode && mode != GET) usage(1); | ||||
| mode = GET; | ||||
| break; | ||||
| case 'F': | ||||
| Fflag = 1; | ||||
| if (mode && mode != GET) usage(1); | ||||
| mode = GET; | ||||
| break; | ||||
| case 'v': | ||||
| vflag = 1; | ||||
| if (mode & SET) usage(1); | ||||
| mode |= GET; | ||||
| break; | ||||
| case 'm': | ||||
| mflag = 1; | ||||
| if (mode & SET) usage(1); | ||||
| mode |= GET; | ||||
| break; | ||||
| case 'f': | ||||
| fflag = 1; | ||||
| if (mode & SET) usage(1); | ||||
| mode |= GET; | ||||
| break; | ||||
| default: | ||||
| fprintf(stderr, "%s: bad option: -%c\n", argv0, ARGC()); | ||||
| usage(1); | ||||
| } ARGEND | ||||
| if (argc > 0) { | ||||
| fprintf(stderr, "%s: bad args:", argv0); | ||||
| for (; *argv; argv++) | ||||
| fprintf(stderr, " %s", argv[0]); | ||||
| fprintf(stderr, "\n"); | ||||
| usage(1); | ||||
| } | ||||
| if (mode == NONE) usage(1); | ||||
| if (mode & GET && !(oflag || tflag || lflag || Oflag || Tflag || Lflag || Pflag || Fflag || cflag || vflag || mflag || fflag)) | ||||
| oflag = tflag = lflag = cflag = vflag = mflag = fflag = Fflag = 1; | ||||
| display = wl_display_connect(NULL); | ||||
| if (!display) die("bad display"); | ||||
| if (Pflag) { | ||||
| int fd = wl_display_get_fd(display); | ||||
| struct ucred ucred; | ||||
| socklen_t len = sizeof(struct ucred); | ||||
| if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) | ||||
| die("bad getsockopt"); | ||||
| printf("%d\n", ucred.pid); | ||||
| } | ||||
| registry = wl_display_get_registry(display); | ||||
| if (!registry) die("bad registry"); | ||||
| wl_registry_add_listener(registry, ®istry_listener, NULL); | ||||
| wl_display_dispatch(display); | ||||
| wl_display_roundtrip(display); | ||||
| if (!dwl_ipc_manager && (mode & SET || tflag || lflag || Tflag || Lflag || cflag || vflag || mflag || fflag)) | ||||
| die("bad dwl-ipc protocol"); | ||||
| wl_display_roundtrip(display); | ||||
| if (Fflag && !(mode == WATCH) && focused_output) | ||||
| printf("focusmon %s\n", focused_output); | ||||
| if (mode == WATCH) | ||||
| while (wl_display_dispatch(display) != -1); | ||||
| return 0; | ||||
| } | ||||
