From 5780d6b7dfc9d2f2d9372819465785e41c0a0b35 Mon Sep 17 00:00:00 2001
From: DreamMaoMao <2523610504@qq.com>
Date: Sat, 18 Oct 2025 12:43:03 +0800
Subject: [PATCH] project: add mmsg as sub project
---
format.sh | 2 +-
meson.build | 3 +-
mmsg/meson.build | 33 ++
mmsg/protocols/dwl-ipc-unstable-v2.xml | 246 ++++++++
mmsg/src/arg.h | 65 +++
mmsg/src/dynarr.h | 30 +
mmsg/src/mmsg.c | 766 +++++++++++++++++++++++++
7 files changed, 1143 insertions(+), 2 deletions(-)
create mode 100644 mmsg/meson.build
create mode 100644 mmsg/protocols/dwl-ipc-unstable-v2.xml
create mode 100644 mmsg/src/arg.h
create mode 100644 mmsg/src/dynarr.h
create mode 100644 mmsg/src/mmsg.c
diff --git a/format.sh b/format.sh
index 743f63e..af4a82a 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
+clang-format -i src/*/*.h -i src/*/*.c -i src/mango.c -i mmsg/src/mmsg.c -i mmsg/src/arg.h -i mmsg/src/dynarr.h
diff --git a/meson.build b/meson.build
index 82206e5..2ab74a1 100644
--- a/meson.build
+++ b/meson.build
@@ -3,6 +3,7 @@ project('mango', ['c', 'cpp'],
)
subdir('protocols')
+subdir('mmsg')
is_nixos = false
os_release = run_command('cat', '/etc/os-release', check: false)
@@ -120,4 +121,4 @@ desktop_install_dir = join_paths(prefix, 'share/wayland-sessions')
install_data('mango.desktop', install_dir : desktop_install_dir)
# 安装 config.conf
-install_data('config.conf', install_dir : join_paths(sysconfdir, 'mango'))
\ No newline at end of file
+install_data('config.conf', install_dir : join_paths(sysconfdir, 'mango'))
diff --git a/mmsg/meson.build b/mmsg/meson.build
new file mode 100644
index 0000000..6d0b1e8
--- /dev/null
+++ b/mmsg/meson.build
@@ -0,0 +1,33 @@
+wayland_scanner = find_program('wayland-scanner')
+
+dwl_ipc_protocol = 'protocols/dwl-ipc-unstable-v2.xml'
+
+dwl_ipc_header = custom_target(
+ 'dwl-ipc-header',
+ input: dwl_ipc_protocol,
+ output: 'dwl-ipc-unstable-v2-protocol.h',
+ command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'],
+ build_by_default: true,
+)
+
+dwl_ipc_source = custom_target(
+ 'dwl-ipc-source',
+ input: dwl_ipc_protocol,
+ output: 'dwl-ipc-unstable-v2-protocol.c',
+ command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'],
+ build_by_default: true,
+)
+
+executable('mmsg',
+ 'src/mmsg.c',
+ dwl_ipc_source,
+ dependencies: [
+ dependency('wayland-client'),
+ ],
+ include_directories: include_directories('.'),
+ install: true,
+ c_args: [
+ '-g',
+ '-Wno-unused-function',
+ ],
+)
\ No newline at end of file
diff --git a/mmsg/protocols/dwl-ipc-unstable-v2.xml b/mmsg/protocols/dwl-ipc-unstable-v2.xml
new file mode 100644
index 0000000..a43a7f0
--- /dev/null
+++ b/mmsg/protocols/dwl-ipc-unstable-v2.xml
@@ -0,0 +1,246 @@
+
+
+
+
+ This protocol allows clients to update and get updates from dwl.
+
+ Warning! The protocol described in this file is experimental and
+ backward incompatible changes may be made. Backward compatible
+ changes may be added together with the corresponding interface
+ version bump.
+ Backward incompatible changes are done by bumping the version
+ number in the protocol and interface names and resetting the
+ interface version. Once the protocol is to be declared stable,
+ the 'z' prefix and the version number in the protocol and
+ interface names are removed and the interface version number is
+ reset.
+
+
+
+
+ This interface is exposed as a global in wl_registry.
+
+ Clients can use this interface to get a dwl_ipc_output.
+ After binding the client will recieve the dwl_ipc_manager.tags and dwl_ipc_manager.layout events.
+ The dwl_ipc_manager.tags and dwl_ipc_manager.layout events expose tags and layouts to the client.
+
+
+
+
+ Indicates that the client will not the dwl_ipc_manager object anymore.
+ Objects created through this instance are not affected.
+
+
+
+
+
+ Get a dwl_ipc_outout for the specified wl_output.
+
+
+
+
+
+
+
+ This event is sent after binding.
+ A roundtrip after binding guarantees the client recieved all tags.
+
+
+
+
+
+
+ This event is sent after binding.
+ A roundtrip after binding guarantees the client recieved all layouts.
+
+
+
+
+
+
+
+ Observe and control a dwl output.
+
+ Events are double-buffered:
+ Clients should cache events and redraw when a dwl_ipc_output.frame event is sent.
+
+ Request are not double-buffered:
+ The compositor will update immediately upon request.
+
+
+
+
+
+
+
+
+
+
+ Indicates to that the client no longer needs this dwl_ipc_output.
+
+
+
+
+
+ Indicates the client should hide or show themselves.
+ If the client is visible then hide, if hidden then show.
+
+
+
+
+
+ Indicates if the output is active. Zero is invalid, nonzero is valid.
+
+
+
+
+
+
+ Indicates that a tag has been updated.
+
+
+
+
+
+
+
+
+
+ Indicates a new layout is selected.
+
+
+
+
+
+
+ Indicates the title has changed.
+
+
+
+
+
+
+ Indicates the appid has changed.
+
+
+
+
+
+
+ Indicates the layout has changed. Since layout symbols are dynamic.
+ As opposed to the zdwl_ipc_manager.layout event, this should take precendence when displaying.
+ You can ignore the zdwl_ipc_output.layout event.
+
+
+
+
+
+
+ Indicates that a sequence of status updates have finished and the client should redraw.
+
+
+
+
+
+
+
+
+
+
+
+ The tags are updated as follows:
+ new_tags = (current_tags AND and_tags) XOR xor_tags
+
+
+
+
+
+
+
+
+
+
+
+ This request allows clients to instruct the compositor to quit mango.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Indicates if the selected client on this output is fullscreen.
+
+
+
+
+
+
+ Indicates if the selected client on this output is floating.
+
+
+
+
+
+
+ Indicates if x coordinates of the selected client.
+
+
+
+
+
+
+ Indicates if y coordinates of the selected client.
+
+
+
+
+
+
+ Indicates if width of the selected client.
+
+
+
+
+
+
+ Indicates if height of the selected client.
+
+
+
+
+
+
+ last map layer.
+
+
+
+
+
+
+ current keyboard layout.
+
+
+
+
+
+
+ current keybind mode.
+
+
+
+
+
+
+
diff --git a/mmsg/src/arg.h b/mmsg/src/arg.h
new file mode 100644
index 0000000..ccbd65a
--- /dev/null
+++ b/mmsg/src/arg.h
@@ -0,0 +1,65 @@
+/*
+ * Copy me if you can.
+ * by 20h
+ */
+
+#ifndef ARG_H__
+#define ARG_H__
+
+extern char *argv0;
+
+/* use main(int argc, char *argv[]) */
+#define ARGBEGIN \
+ for (argv0 = *argv, argv++, argc--; \
+ argv[0] && argv[0][0] == '-' && argv[0][1]; argc--, argv++) { \
+ char argc_; \
+ char **argv_; \
+ int 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/src/dynarr.h b/mmsg/src/dynarr.h
new file mode 100644
index 0000000..45cd356
--- /dev/null
+++ b/mmsg/src/dynarr.h
@@ -0,0 +1,30 @@
+#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/src/mmsg.c b/mmsg/src/mmsg.c
new file mode 100644
index 0000000..ae9270d
--- /dev/null
+++ b/mmsg/src/mmsg.c
@@ -0,0 +1,766 @@
+#include "arg.h"
+#include "dwl-ipc-unstable-v2-protocol.h"
+#include "dynarr.h"
+#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 int Oflag;
+static int Tflag;
+static int Lflag;
+static int oflag;
+static int tflag;
+static int lflag;
+static int cflag;
+static int vflag;
+static int mflag;
+static int fflag;
+static int qflag;
+static int dflag;
+static int xflag;
+static int eflag;
+static int kflag;
+static int bflag;
+
+static uint32_t occ, seltags, total_clients, urg;
+
+static char *output_name;
+static int tagcount;
+static char *tagset;
+static char *layout_name;
+static int 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, unsigned int n) {
+ for (int i = 8; i >= 0; i--) {
+ *buf++ = ((n >> i) & 1) ? '1' : '0';
+ }
+ *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;
+ }
+ 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_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;
+ int 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;
+ int 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);
+ 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;
+ }
+ }
+ 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,
+ .frame = dwl_ipc_output_frame,
+};
+
+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 (Oflag)
+ printf("%s\n", name);
+ if (output_name && strcmp(output_name, name) != 0) {
+ wl_output_release(output);
+ return;
+ }
+ 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);
+ }
+}
+
+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);
+ }
+ }
+}
+
+static const struct wl_registry_listener registry_listener = {
+ .global = global_add,
+ .global_remove = global_remove,
+};
+
+static void usage(void) {
+ fprintf(stderr,
+ "usage:"
+ "\t%s [-OTLq]\n"
+ "\t%s [-o