Files
swindle/smsg/smsg.c
T
tsukasa 01a1dbec8e init
2026-07-12 01:07:23 +08:00

517 lines
12 KiB
C

#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, &registry_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;
}