Skip to content

Instantly share code, notes, and snippets.

@Yappaholic
Last active September 29, 2024 09:33
Show Gist options
  • Select an option

  • Save Yappaholic/e9c74264d1bee34e7667b256f187b0c3 to your computer and use it in GitHub Desktop.

Select an option

Save Yappaholic/e9c74264d1bee34e7667b256f187b0c3 to your computer and use it in GitHub Desktop.
Dwlmsg patch for eww widgets in dwl window manager.
This is a dwlmsg patch for the dwl window manager.
Makes it possible to use Elkowar Wacky Widgets with dwlmsg.
The original patch is in the dwl Discord, big thanks to @.thecyberduck and @nothcoc.
Don't know why, but compiling after patch with clang and LTO throws linker error,
so build dwl without LTO support afterwards.
--- a/Makefile 2024-09-29 12:01:37.541046059 +0300
+++ b/Makefile 2024-09-29 12:13:24.312054194 +0300
@@ -16,14 +16,18 @@
DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(WLR_INCS) $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS)
LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(WLR_LIBS) -lm $(LIBS)
-all: dwl
+all: dwl dwlmsg
dwl: dwl.o util.o dwl-ipc-unstable-v2-protocol.o
$(CC) dwl.o util.o dwl-ipc-unstable-v2-protocol.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@
dwl.o: dwl.c client.h config.h config.mk cursor-shape-v1-protocol.h \
pointer-constraints-unstable-v1-protocol.h wlr-layer-shell-unstable-v1-protocol.h \
wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h \
dwl-ipc-unstable-v2-protocol.h
+dwlmsg: dwlmsg.o util.o dwl-ipc-unstable-v2-protocol.o
+ $(CC) $^ -lwayland-client -Wno-strict-prototypes $(DWLCFLAGS) -o $@
+dwlmsg.o: dwlmsg.c dwl-ipc-unstable-v2-client-protocol.h
util.o: util.c util.h
+dwl-ipc-unstable-v2-protocol-client.o: dwl-ipc-unstable-v2-client-protocol.h
dwl-ipc-unstable-v2-protocol.o: dwl-ipc-unstable-v2-protocol.c dwl-ipc-unstable-v2-protocol.h
# wayland-scanner is a tool which generates C headers and rigging for Wayland
@@ -50,6 +54,9 @@
dwl-ipc-unstable-v2-protocol.h:
$(WAYLAND_SCANNER) server-header \
protocols/dwl-ipc-unstable-v2.xml $@
+dwl-ipc-unstable-v2-client-protocol.h:
+ $(WAYLAND_SCANNER) client-header \
+ protocols/dwl-ipc-unstable-v2.xml $@
dwl-ipc-unstable-v2-protocol.c:
$(WAYLAND_SCANNER) private-code \
protocols/dwl-ipc-unstable-v2.xml $@
@@ -57,20 +64,22 @@
config.h:
cp config.def.h $@
clean:
- rm -f dwl *.o *-protocol.h
+ rm -f dwl dwlmsg *.o *-protocol.h
dist: clean
mkdir -p dwl-$(VERSION)
cp -R LICENSE* Makefile CHANGELOG.md README.md client.h config.def.h \
- config.mk protocols dwl.1 dwl.c util.c util.h dwl.desktop \
+ config.mk protocols dwl.1 dwl.c dwlmsg.c util.c util.h dwl.desktop \
dwl-$(VERSION)
tar -caf dwl-$(VERSION).tar.gz dwl-$(VERSION)
rm -rf dwl-$(VERSION)
-install: dwl
+install: dwl dwlmsg
mkdir -p $(DESTDIR)$(PREFIX)/bin
cp -f dwl $(DESTDIR)$(PREFIX)/bin
chmod 755 $(DESTDIR)$(PREFIX)/bin/dwl
+ cp -f dwlmsg $(DESTDIR)$(PREFIX)/bin
+ chmod 755 $(DESTDIR)$(PREFIX)/bin/dwlmsg
mkdir -p $(DESTDIR)$(MANDIR)/man1
cp -f dwl.1 $(DESTDIR)$(MANDIR)/man1
chmod 644 $(DESTDIR)$(MANDIR)/man1/dwl.1
--- /dev/null 2024-09-29 10:54:55.245091132 +0300
+++ b/dwlmsg.c 2024-09-29 12:13:24.313054194 +0300
@@ -0,0 +1,546 @@
+#include <getopt.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wayland-client-core.h>
+#include <wayland-client-protocol.h>
+#include <wayland-client.h>
+#include <wayland-util.h>
+#include "util.h"
+#include "dwl-ipc-unstable-v2-client-protocol.h"
+
+/*
+ * This is a modified version of the original `dwlmsg` program by notchoc at
+ * https://codeberg.org/notchoc/dwlmsg. All credits goes to notchoc for his
+ * excellent work.
+
+ * This version brings json fomatted output. However this is limited to single
+ * monitor setup. This may or may not work on multi monitor systems. If you test
+ * let me know the result. As of now it is not tested on multimonitor systems
+ * (I do not own multimonitor setup currently).
+
+ * You are free to modify, use and publish it however you wish, with or without
+ * my permission.
+ */
+
+static enum {
+ NONE = 0,
+ GET = 1 << 0,
+ SET = 1 << 1,
+ WATCH = 1 << 2 | GET,
+} mode = NONE;
+
+struct output {
+ char *name; /* output name */
+ uint32_t id; /* registry interface id */
+ uint32_t active; /* output is active or down */
+
+ uint32_t tags[9][3]; /* replace 9 with the value of TAGCOUNT in dwl */
+
+ uint32_t layout; /* layout: 0 -> tile, 1 -> float, ... */
+ char *layoutSymbol; /* layout name */
+
+ char *title; /* focused client title */
+ char *appid; /* focused client appid */
+ uint32_t fullscreen; /* is focused client fullscreen */
+ uint32_t floating; /* is focused client floating */
+
+ struct wl_list link;
+};
+
+/* variables */
+static char *arg_output, *arg_tagset, *arg_ctagset, *arg_layout;
+static size_t layoutcount, layout_idx;
+static uint32_t tagcount, seltags;
+static unsigned int cflag, lflag, tflag, oflag;
+static struct wl_list outputs;
+static struct wl_display *display;
+static struct wl_registry *registry;
+static struct zdwl_ipc_manager_v2 *dwl_ipc_manager;
+
+/* function definitions */
+static void
+dwl_ipc_tags(void *data, struct zdwl_ipc_manager_v2 *ipc_manager, uint32_t count)
+{
+ tagcount = count;
+}
+
+static void
+dwl_ipc_layout(void *data, struct zdwl_ipc_manager_v2 *ipc_manager, const char *name)
+{
+ layoutcount++;
+ if (lflag && (mode & SET) && strcmp(arg_layout, name) == 0)
+ layout_idx = 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) {}
+
+static void
+dwl_ipc_output_active(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, uint32_t active)
+{
+ struct output *output;
+
+ if (!oflag || !(mode & GET))
+ return;
+
+ wl_list_for_each(output, &outputs, link)
+ if (output->name == data) {
+ output->active = 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)
+{
+ struct output *output;
+
+ if (!tflag)
+ return;
+
+ if (mode & SET) {
+ if (state != ZDWL_IPC_OUTPUT_V2_TAG_STATE_NONE)
+ seltags |= 1<<tag;
+ return;
+ }
+
+ if (!(mode & GET))
+ return;
+
+ wl_list_for_each(output, &outputs, link)
+ if (output->name == data) {
+ output->tags[tag][0] = state;
+ output->tags[tag][1] = clients;
+ output->tags[tag][2] = focused;
+ }
+}
+
+static void
+dwl_ipc_output_layout(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output,
+ uint32_t layout)
+{
+ struct output *output;
+
+ if (!lflag || !(mode & GET))
+ return;
+
+ wl_list_for_each(output, &outputs, link)
+ if (output->name == data) {
+ output->layout = layout;
+ }
+}
+
+static void
+dwl_ipc_output_layout_symbol(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output,
+ const char *layout)
+{
+ struct output *output;
+
+ if (!lflag || !(mode & GET))
+ return;
+
+ wl_list_for_each(output, &outputs, link)
+ if (output->name == data) {
+ output->layoutSymbol = strdup(layout);
+ }
+}
+
+static void
+dwl_ipc_output_title(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output,
+ const char *title)
+{
+ struct output *output;
+
+ if (!cflag || !(mode & GET))
+ return;
+
+ wl_list_for_each(output, &outputs, link)
+ if (output->name == data) {
+ output->title = strdup(title);
+ }
+}
+
+static void
+dwl_ipc_output_appid(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output,
+ const char *appid)
+{
+ struct output *output;
+
+ if (!cflag || !(mode & GET))
+ return;
+
+ wl_list_for_each(output, &outputs, link)
+ if (output->name == data) {
+ output->appid = strdup(appid);
+ }
+}
+
+static void
+dwl_ipc_output_fullscreen(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output,
+ uint32_t is_fullscreen)
+{
+ struct output *output;
+
+ if (!cflag || !(mode & GET))
+ return;
+
+ wl_list_for_each(output, &outputs, link)
+ if (output->name == data) {
+ output->fullscreen = is_fullscreen;
+ }
+}
+
+static void
+dwl_ipc_output_floating(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output,
+ uint32_t is_floating)
+{
+ struct output *output;
+
+ if (!cflag || !(mode & GET)) return;
+
+ wl_list_for_each(output, &outputs, link)
+ if (output->name == data) {
+ output->floating = is_floating;
+ }
+}
+
+static void
+dwl_ipc_output_frame(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output)
+{
+ struct output *output;
+
+ if (mode & SET) {
+ if (cflag) {
+ uint32_t and = ~0, xor = 0;
+ char *t = arg_ctagset;
+ uint32_t i = 0;
+
+ for (; *t && *t >= '0' && *t <= '9'; t++)
+ i = *t-'0' + i*10;
+ if (i < 1 || i > tagcount)
+ die("invalid tagset %s", arg_ctagset);
+ i--;
+
+ 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;
+ }
+ }
+
+ zdwl_ipc_output_v2_set_client_tags(dwl_ipc_output, and, xor);
+ }
+ if (tflag) {
+ int toggle;
+ char *t;
+ uint32_t i, mask;
+
+ i = 0;
+ toggle = 1;
+ t = arg_tagset;
+ mask = seltags;
+
+ if (*t == '!') {
+ toggle = 0;
+ t++;
+ }
+
+ for (; *t && *t >= '0' && *t <= '9'; t++)
+ i = *t-'0' + i*10;
+ if (i < 1 || i > tagcount)
+ die("invalid tagset %s", arg_tagset);
+ i--;
+
+ 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;
+ }
+ }
+
+ zdwl_ipc_output_v2_set_tags(dwl_ipc_output, mask, toggle);
+ }
+ if (lflag) {
+ if (layout_idx == 0) {
+ for (char *c = arg_layout; *c; c++) {
+ if (*c < '0' || *c > '9')
+ die("invalid layout %s", arg_layout);
+ layout_idx = *c-'0' + layout_idx*10;
+ }
+ }
+ if (layout_idx < 1 || layout_idx > layoutcount)
+ die("invalid layout %s", arg_layout);
+ zdwl_ipc_output_v2_set_layout(dwl_ipc_output, layout_idx-1);
+ }
+
+ wl_display_flush(display);
+ return;
+ }
+
+ wl_list_for_each(output, &outputs, link) {
+ if (output->name == data) {
+ printf("{ \"output\": \"%s\"", output->name);
+ if (oflag)
+ printf(", \"registryId\": %u, \"active\": %s",
+ output->id, output->active ? "true" : "false");
+ if (tflag) {
+ printf(", \"tags\": [");
+ for (uint32_t i=0; i<tagcount; i++) {
+ printf("{ \"tag\": %u, \"state\": %u, "
+ "\"clients\": %u, \"focused\": %u }%s",
+ i+1, output->tags[i][0], output->tags[i][1],
+ output->tags[i][2], (i<tagcount-1) ? ", " : "");
+ }
+ printf("]");
+ }
+ if (lflag)
+ printf(", \"layout\": %u, \"layoutSymbol\": \"%s\"",
+ output->layout, output->layoutSymbol);
+ if (cflag)
+ printf(", \"title\": \"%s\""
+ ", \"appid\": \"%s\""
+ ", \"fullscreen\": \"%u\""
+ ", \"floating\": \"%u\"",
+ output->title, output->appid,
+ output->fullscreen, output->floating);
+ printf(" }\n");
+ }
+ }
+
+ 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_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
+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_name(void *data, struct wl_output *wl_output, const char *name)
+{
+ uint32_t *id;
+ struct output *output;
+
+ if (arg_output && strcmp(arg_output, name) != 0) {
+ wl_output_release(wl_output);
+ return;
+ }
+
+ id = data;
+ wl_list_for_each(output, &outputs, link)
+ if (output->id == *id) {
+ output->name = strdup(name);
+ break;
+ }
+
+ if (dwl_ipc_manager) {
+ struct zdwl_ipc_output_v2 *dwl_ipc_output = zdwl_ipc_manager_v2_get_output(dwl_ipc_manager, wl_output);
+ zdwl_ipc_output_v2_add_listener(dwl_ipc_output, &dwl_ipc_output_listener, output->name);
+ }
+}
+
+static void
+wl_output_description(void *data, struct wl_output *wl_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_registry_add(void *data, struct wl_registry *wl_registry,
+ uint32_t name, const char *interface, uint32_t version)
+{
+ struct output *o;
+ struct wl_output *wl_output;
+
+ if (strcmp(interface, wl_output_interface.name) == 0) {
+ o = ecalloc(1, sizeof(struct output));
+ o->id = name;
+ wl_list_insert(&outputs, &o->link); /* store output in list of outputs */
+
+ wl_output = wl_registry_bind(
+ wl_registry, name, &wl_output_interface,
+ WL_OUTPUT_NAME_SINCE_VERSION);
+ wl_output_add_listener(wl_output, &output_listener, &o->id);
+ } 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_registry_remove(void *data, struct wl_registry *wl_registry, uint32_t name)
+{
+ struct output *output;
+
+ wl_list_for_each(output, &outputs, link)
+ if (output->id == name) {
+ wl_list_remove(&output->link);
+ free(output);
+ break;
+ }
+}
+
+static const struct wl_registry_listener registry_listener = {
+ .global = global_registry_add,
+ .global_remove = global_registry_remove,
+};
+
+static void
+usage(int code)
+{
+ fprintf(code ? stderr : stdout,
+ "usage:"
+ "\tdwlmsg [-m <monitor>] (-g | -w) [-clot]\n"
+ "\tdwlmsg [-m <monitor>] -s ([-c<tags>] [-t<tags>] [-l<layout>])\n");
+ exit(code);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+
+ while ((c = getopt(argc, argv, "c::ghl::m:ost::w")) != -1) {
+ switch(c) {
+ case 'c':
+ cflag = 1;
+ if (optarg) arg_ctagset = optarg;
+ break;
+ case 'g':
+ if (mode != NONE) usage(1);
+ mode = GET;
+ break;
+ case 'h':
+ usage(0);
+ break;
+ case 'm':
+ arg_output = optarg;
+ break;
+ case 'l':
+ lflag = 1;
+ if (optarg) arg_layout = optarg;
+ break;
+ case 'o':
+ oflag = 1;
+ break;
+ case 's':
+ if (mode != NONE) usage(1);
+ mode = SET;
+ break;
+ case 'w':
+ if (mode != NONE) usage(1);
+ mode = WATCH;
+ break;
+ case 't':
+ tflag = 1;
+ if (optarg) arg_tagset = optarg;
+ break;
+ case '?':
+ usage(1);
+ break;
+ }
+ }
+
+ if (mode == NONE)
+ usage(1);
+ if ((mode & GET) && (!(cflag|lflag|oflag|tflag)))
+ cflag = lflag = oflag = tflag = 1;
+ if ((mode & SET) && (!(cflag|lflag|tflag)))
+ usage(1);
+ if (mode & GET) {
+ if ((tflag && arg_tagset)
+ || (cflag && arg_ctagset)
+ || (lflag && arg_layout))
+ usage(1);
+ }
+ if (mode & SET) {
+ if ((tflag && !arg_tagset)
+ || (cflag && !arg_ctagset)
+ || (lflag && !arg_layout))
+ usage(1);
+ }
+
+ display = wl_display_connect(NULL);
+ if (!display)
+ die("could not connect to display");
+
+ registry = wl_display_get_registry(display);
+ if (!registry)
+ die("could not get registry");
+
+ wl_list_init(&outputs);
+
+ wl_registry_add_listener(registry, &registry_listener, NULL);
+
+ wl_display_dispatch(display);
+ wl_display_roundtrip(display);
+
+ if (!dwl_ipc_manager)
+ die("dwl ipc protocol is not present");
+
+ wl_display_roundtrip(display);
+
+ if (mode == WATCH)
+ while (wl_display_dispatch(display) != -1);
+
+ return 0;
+}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment