bar-systray: Support non-linux

- Remove unportable code: use self-pipe for waking up the wl_event_loop
  on dbus events instead of eventfd. Tested on FreeBSD.

- Don't die if another tray is already running. Previous version didn't
  allow nested dwls.
This commit is contained in:
vetu104 2025-03-29 19:24:28 +02:00
parent 4df7dc7376
commit 7d54a01970

View File

@ -1,14 +1,14 @@
From b5b0214d74ad8eec0cf2da0a3f2afcea8245a782 Mon Sep 17 00:00:00 2001 From e40de8cb1f33ebd7978f7f7843aa94ee241cb55a Mon Sep 17 00:00:00 2001
From: vetu104 <vetu104@proton.me> From: vetu104 <vetu104@proton.me>
Date: Sat, 8 Mar 2025 14:35:42 +0200 Date: Sat, 29 Mar 2025 19:22:37 +0200
Subject: [PATCH] Add a system tray next to sewn's bar Subject: [PATCH] Add a system tray next to sewn's bar
--- ---
Makefile | 23 +- Makefile | 23 +-
config.def.h | 5 + config.def.h | 5 +
dbus.c | 240 +++++++++++++++ dbus.c | 242 +++++++++++++++
dbus.h | 10 + dbus.h | 10 +
dwl.c | 109 ++++++- dwl.c | 107 ++++++-
systray/helpers.c | 43 +++ systray/helpers.c | 43 +++
systray/helpers.h | 12 + systray/helpers.h | 12 +
systray/icon.c | 149 +++++++++ systray/icon.c | 149 +++++++++
@ -19,9 +19,9 @@ Subject: [PATCH] Add a system tray next to sewn's bar
systray/menu.h | 11 + systray/menu.h | 11 +
systray/tray.c | 237 +++++++++++++++ systray/tray.c | 237 +++++++++++++++
systray/tray.h | 37 +++ systray/tray.h | 37 +++
systray/watcher.c | 549 +++++++++++++++++++++++++++++++++ systray/watcher.c | 551 +++++++++++++++++++++++++++++++++
systray/watcher.h | 34 +++ systray/watcher.h | 35 +++
17 files changed, 2678 insertions(+), 13 deletions(-) 17 files changed, 2681 insertions(+), 13 deletions(-)
create mode 100644 dbus.c create mode 100644 dbus.c
create mode 100644 dbus.h create mode 100644 dbus.h
create mode 100644 systray/helpers.c create mode 100644 systray/helpers.c
@ -114,46 +114,53 @@ index 5d1dc2b..451643e 100644
}; };
diff --git a/dbus.c b/dbus.c diff --git a/dbus.c b/dbus.c
new file mode 100644 new file mode 100644
index 0000000..653a133 index 0000000..125312c
--- /dev/null --- /dev/null
+++ b/dbus.c +++ b/dbus.c
@@ -0,0 +1,240 @@ @@ -0,0 +1,242 @@
+#include "dbus.h" +#include "dbus.h"
+ +
+#include "util.h"
+
+#include <dbus/dbus.h> +#include <dbus/dbus.h>
+#include <stdlib.h>
+#include <wayland-server-core.h> +#include <wayland-server-core.h>
+ +
+#include <fcntl.h>
+#include <stddef.h> +#include <stddef.h>
+#include <stdint.h> +#include <stdint.h>
+#include <stdio.h> +#include <stdio.h>
+#if defined __linux__
+#include <sys/eventfd.h>
+#elif defined(__FreeBSD__) || defined(__OpenBSD__)
+#include <sys/event.h>
+#endif
+#include <unistd.h> +#include <unistd.h>
+ +
+int efd = -1; +static void
+close_pipe(void *data)
+{
+ int *pipefd = data;
+
+ close(pipefd[0]);
+ close(pipefd[1]);
+ free(pipefd);
+}
+ +
+static int +static int
+dwl_dbus_dispatch(int fd, unsigned int mask, void *data) +dwl_dbus_dispatch(int fd, unsigned int mask, void *data)
+{ +{
+ DBusConnection *conn = data; + DBusConnection *conn = data;
+ +
+ uint64_t dispatch_pending; + int pending;
+ DBusDispatchStatus status; + DBusDispatchStatus oldstatus, newstatus;
+ +
+ status = dbus_connection_dispatch(conn); + oldstatus = dbus_connection_get_dispatch_status(conn);
+ newstatus = dbus_connection_dispatch(conn);
+ +
+ /* + /* Don't clear pending flag if status didn't change */
+ * Don't clear pending flag if message queue wasn't + if (oldstatus == newstatus)
+ * fully drained
+ */
+ if (status != DBUS_DISPATCH_COMPLETE)
+ return 0; + return 0;
+ +
+ if (read(fd, &dispatch_pending, sizeof(uint64_t)) < 0) + if (read(fd, &pending, sizeof(int)) < 0) {
+ perror("read"); + perror("read");
+ die("Error in dbus dispatch");
+ }
+ +
+ return 0; + return 0;
+} +}
@ -242,8 +249,8 @@ index 0000000..653a133
+ +
+ interval = dbus_timeout_get_interval(timeout); + interval = dbus_timeout_get_interval(timeout);
+ +
+ timeout_source = wl_event_loop_add_timer( + timeout_source =
+ loop, dwl_dbus_timeout_handle, timeout); + wl_event_loop_add_timer(loop, dwl_dbus_timeout_handle, timeout);
+ +
+ r = wl_event_source_timer_update(timeout_source, interval); + r = wl_event_source_timer_update(timeout_source, interval);
+ if (r < 0) { + if (r < 0) {
@ -270,77 +277,74 @@ index 0000000..653a133
+} +}
+ +
+static void +static void
+dwl_dbus_adjust_timeout(DBusTimeout *timeout, void *data) +dwl_dbus_dispatch_status(DBusConnection *conn, DBusDispatchStatus status,
+ void *data)
+{ +{
+ int interval; + int *pipefd = data;
+ struct wl_event_source *timeout_source;
+ +
+ timeout_source = dbus_timeout_get_data(timeout); + if (status != DBUS_DISPATCH_COMPLETE) {
+ + int pending = 1;
+ if (timeout_source) { + if (write(pipefd[1], &pending, sizeof(int)) < 0) {
+ interval = dbus_timeout_get_interval(timeout);
+ wl_event_source_timer_update(timeout_source, interval);
+ }
+}
+
+static void
+dwl_dbus_dispatch_status(DBusConnection *conn, DBusDispatchStatus status, void *data)
+{
+ if (status == DBUS_DISPATCH_DATA_REMAINS) {
+ uint64_t dispatch_pending = 1;
+ if (write(efd, &dispatch_pending, sizeof(uint64_t)) < 0)
+ perror("write"); + perror("write");
+ die("Error in dispatch status");
+ }
+ } + }
+} +}
+ +
+struct wl_event_source * +struct wl_event_source *
+startbus(DBusConnection *conn, struct wl_event_loop *loop) +startbus(DBusConnection *conn, struct wl_event_loop *loop)
+{ +{
+ int *pipefd;
+ int pending = 1, flags;
+ struct wl_event_source *bus_source = NULL; + struct wl_event_source *bus_source = NULL;
+ uint64_t dispatch_pending = 1; +
+ pipefd = ecalloc(2, sizeof(int));
+
+ /*
+ * Libdbus forbids calling dbus_connection_dipatch from the
+ * DBusDispatchStatusFunction directly. Notify the event loop of
+ * updates via a self-pipe.
+ */
+ if (pipe(pipefd) < 0)
+ goto fail;
+ if (((flags = fcntl(pipefd[0], F_GETFD)) < 0) ||
+ fcntl(pipefd[0], F_SETFD, flags | FD_CLOEXEC) < 0 ||
+ ((flags = fcntl(pipefd[1], F_GETFD)) < 0) ||
+ fcntl(pipefd[1], F_SETFD, flags | FD_CLOEXEC) < 0) {
+ goto fail;
+ }
+ +
+ dbus_connection_set_exit_on_disconnect(conn, FALSE); + dbus_connection_set_exit_on_disconnect(conn, FALSE);
+ +
+#if defined __linux__ + bus_source = wl_event_loop_add_fd(loop, pipefd[0], WL_EVENT_READABLE,
+ efd = eventfd(0, EFD_CLOEXEC); + dwl_dbus_dispatch, conn);
+#elif defined(__FreeBSD__) || defined(__OpenBSD__)
+ efd = kqueue();
+#endif
+ if (efd < 0)
+ goto fail;
+
+ dbus_connection_set_dispatch_status_function(conn, dwl_dbus_dispatch_status, NULL, NULL);
+
+ if (!dbus_connection_set_watch_functions(conn, dwl_dbus_add_watch,
+ dwl_dbus_remove_watch,
+ NULL, loop, NULL)) {
+ goto fail;
+ }
+
+ if (!dbus_connection_set_timeout_functions(
+ conn, dwl_dbus_add_timeout, dwl_dbus_remove_timeout,
+ dwl_dbus_adjust_timeout, loop, NULL)) {
+ goto fail;
+ }
+
+ bus_source = wl_event_loop_add_fd(loop, efd, WL_EVENT_READABLE, dwl_dbus_dispatch, conn);
+ if (!bus_source) + if (!bus_source)
+ goto fail; + goto fail;
+ +
+ if (dbus_connection_get_dispatch_status(conn) == DBUS_DISPATCH_DATA_REMAINS) + dbus_connection_set_dispatch_status_function(conn,
+ if (write(efd, &dispatch_pending, sizeof(uint64_t)) < 0) + dwl_dbus_dispatch_status,
+ perror("write"); + pipefd, close_pipe);
+ if (!dbus_connection_set_watch_functions(conn, dwl_dbus_add_watch,
+ dwl_dbus_remove_watch, NULL,
+ loop, NULL)) {
+ goto fail;
+ }
+ if (!dbus_connection_set_timeout_functions(conn, dwl_dbus_add_timeout,
+ dwl_dbus_remove_timeout,
+ NULL, loop, NULL)) {
+ goto fail;
+ }
+ if (dbus_connection_get_dispatch_status(conn) != DBUS_DISPATCH_COMPLETE)
+ if (write(pipefd[1], &pending, sizeof(int)) < 0)
+ goto fail;
+ +
+ return bus_source; + return bus_source;
+ +
+fail: +fail:
+ if (bus_source) + if (bus_source)
+ wl_event_source_remove(bus_source); + wl_event_source_remove(bus_source);
+ if (efd >= 0) { + dbus_connection_set_timeout_functions(conn, NULL, NULL, NULL, NULL,
+ close(efd); + NULL);
+ efd = -1;
+ }
+ dbus_connection_set_timeout_functions(conn, NULL, NULL, NULL, NULL, NULL);
+ dbus_connection_set_watch_functions(conn, NULL, NULL, NULL, NULL, NULL); + dbus_connection_set_watch_functions(conn, NULL, NULL, NULL, NULL, NULL);
+ dbus_connection_set_dispatch_status_function(conn, NULL, NULL, NULL); + dbus_connection_set_dispatch_status_function(conn, NULL, NULL, NULL);
+ +
@ -351,11 +355,9 @@ index 0000000..653a133
+stopbus(DBusConnection *conn, struct wl_event_source *bus_source) +stopbus(DBusConnection *conn, struct wl_event_source *bus_source)
+{ +{
+ wl_event_source_remove(bus_source); + wl_event_source_remove(bus_source);
+ close(efd);
+ efd = -1;
+
+ dbus_connection_set_watch_functions(conn, NULL, NULL, NULL, NULL, NULL); + dbus_connection_set_watch_functions(conn, NULL, NULL, NULL, NULL, NULL);
+ dbus_connection_set_timeout_functions(conn, NULL, NULL, NULL, NULL, NULL); + dbus_connection_set_timeout_functions(conn, NULL, NULL, NULL, NULL,
+ NULL);
+ dbus_connection_set_dispatch_status_function(conn, NULL, NULL, NULL); + dbus_connection_set_dispatch_status_function(conn, NULL, NULL, NULL);
+} +}
diff --git a/dbus.h b/dbus.h diff --git a/dbus.h b/dbus.h
@ -375,7 +377,7 @@ index 0000000..b374b98
+ +
+#endif /* DWLDBUS_H */ +#endif /* DWLDBUS_H */
diff --git a/dwl.c b/dwl.c diff --git a/dwl.c b/dwl.c
index ece537a..959cc50 100644 index ece537a..7753ef6 100644
--- a/dwl.c --- a/dwl.c
+++ b/dwl.c +++ b/dwl.c
@@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
@ -429,7 +431,7 @@ index ece537a..959cc50 100644
+static DBusConnection *bus_conn; +static DBusConnection *bus_conn;
+static struct wl_event_source *bus_source; +static struct wl_event_source *bus_source;
+static Watcher watcher; +static Watcher watcher = {.running = 0};
+ +
static const struct wlr_buffer_impl buffer_impl = { static const struct wlr_buffer_impl buffer_impl = {
.destroy = bufdestroy, .destroy = bufdestroy,
@ -453,20 +455,16 @@ index ece537a..959cc50 100644
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
@@ -750,17 +763,29 @@ buttonpress(struct wl_listener *listener, void *data) @@ -751,6 +764,8 @@ buttonpress(struct wl_listener *listener, void *data)
if (!c && !exclusive_focus &&
(node = wlr_scene_node_at(&layers[LyrBottom]->node, cursor->x, cursor->y, NULL, NULL)) && (node = wlr_scene_node_at(&layers[LyrBottom]->node, cursor->x, cursor->y, NULL, NULL)) &&
(buffer = wlr_scene_buffer_from_node(node)) && buffer == selmon->scene_buffer) { (buffer = wlr_scene_buffer_from_node(node)) && buffer == selmon->scene_buffer) {
+
cx = (cursor->x - selmon->m.x) * selmon->wlr_output->scale; cx = (cursor->x - selmon->m.x) * selmon->wlr_output->scale;
+ traywidth = tray_get_width(selmon->tray); + traywidth = tray_get_width(selmon->tray);
+ +
do do
x += TEXTW(selmon, tags[i]); x += TEXTW(selmon, tags[i]);
while (cx >= x && ++i < LENGTH(tags)); while (cx >= x && ++i < LENGTH(tags));
+ @@ -759,8 +774,16 @@ buttonpress(struct wl_listener *listener, void *data)
if (i < LENGTH(tags)) {
click = ClkTagBar;
arg.ui = 1 << i; arg.ui = 1 << i;
} else if (cx < x + TEXTW(selmon, selmon->ltsymbol)) } else if (cx < x + TEXTW(selmon, selmon->ltsymbol))
click = ClkLtSymbol; click = ClkLtSymbol;
@ -484,7 +482,7 @@ index ece537a..959cc50 100644
} else } else
click = ClkTitle; click = ClkTitle;
} }
@@ -774,7 +799,12 @@ buttonpress(struct wl_listener *listener, void *data) @@ -774,7 +797,12 @@ buttonpress(struct wl_listener *listener, void *data)
mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
for (b = buttons; b < END(buttons); b++) { for (b = buttons; b < END(buttons); b++) {
if (CLEANMASK(mods) == CLEANMASK(b->mod) && event->button == b->button && click == b->click && b->func) { if (CLEANMASK(mods) == CLEANMASK(b->mod) && event->button == b->button && click == b->click && b->func) {
@ -498,12 +496,14 @@ index ece537a..959cc50 100644
return; return;
} }
} }
@@ -840,6 +870,12 @@ cleanup(void) @@ -840,6 +868,14 @@ cleanup(void)
destroykeyboardgroup(&kb_group->destroy, NULL); destroykeyboardgroup(&kb_group->destroy, NULL);
+ if (showbar && showsystray) { + if (watcher.running)
+ watcher_stop(&watcher); + watcher_stop(&watcher);
+
+ if (showbar && showsystray) {
+ stopbus(bus_conn, bus_source); + stopbus(bus_conn, bus_source);
+ dbus_connection_unref(bus_conn); + dbus_connection_unref(bus_conn);
+ } + }
@ -596,25 +596,23 @@ index ece537a..959cc50 100644
void void
drawbars(void) drawbars(void)
{ {
@@ -2818,6 +2889,17 @@ setup(void) @@ -2818,6 +2889,15 @@ setup(void)
status_event_source = wl_event_loop_add_fd(wl_display_get_event_loop(dpy), status_event_source = wl_event_loop_add_fd(wl_display_get_event_loop(dpy),
STDIN_FILENO, WL_EVENT_READABLE, statusin, NULL); STDIN_FILENO, WL_EVENT_READABLE, statusin, NULL);
+ if (showbar && showsystray) { + bus_conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
+ bus_conn = dbus_bus_get(DBUS_BUS_SESSION, NULL); + if (!bus_conn)
+ if (!bus_conn) + die("Failed to connect to bus");
+ die("Failed to connect to bus"); + bus_source = startbus(bus_conn, event_loop);
+ bus_source = startbus(bus_conn, event_loop); + if (!bus_source)
+ if (!bus_source) + die("Failed to start listening to bus events");
+ die("Failed to start listening to bus events"); + if (showbar && showsystray)
+ if (watcher_start(&watcher, bus_conn, event_loop) < 0) + watcher_start(&watcher, bus_conn, event_loop);
+ die("Failed to start tray watcher");
+ }
+ +
/* Make sure XWayland clients don't connect to the parent X server, /* Make sure XWayland clients don't connect to the parent X server,
* e.g when running in the x11 backend or the wayland backend and the * e.g when running in the x11 backend or the wayland backend and the
* compositor has Xwayland support */ * compositor has Xwayland support */
@@ -3160,6 +3242,7 @@ updatebar(Monitor *m) @@ -3160,6 +3240,7 @@ updatebar(Monitor *m)
size_t i; size_t i;
int rw, rh; int rw, rh;
char fontattrs[12]; char fontattrs[12];
@ -622,7 +620,7 @@ index ece537a..959cc50 100644
wlr_output_transformed_resolution(m->wlr_output, &rw, &rh); wlr_output_transformed_resolution(m->wlr_output, &rw, &rh);
m->b.width = rw; m->b.width = rw;
@@ -3185,6 +3268,18 @@ updatebar(Monitor *m) @@ -3185,6 +3266,18 @@ updatebar(Monitor *m)
m->lrpad = m->drw->font->height; m->lrpad = m->drw->font->height;
m->b.height = m->drw->font->height + 2; m->b.height = m->drw->font->height + 2;
m->b.real_height = (int)((float)m->b.height / m->wlr_output->scale); m->b.real_height = (int)((float)m->b.height / m->wlr_output->scale);
@ -2424,10 +2422,10 @@ index 0000000..af4e5e3
+#endif /* TRAY_H */ +#endif /* TRAY_H */
diff --git a/systray/watcher.c b/systray/watcher.c diff --git a/systray/watcher.c b/systray/watcher.c
new file mode 100644 new file mode 100644
index 0000000..072ab86 index 0000000..8dd84b9
--- /dev/null --- /dev/null
+++ b/systray/watcher.c +++ b/systray/watcher.c
@@ -0,0 +1,549 @@ @@ -0,0 +1,551 @@
+#include "watcher.h" +#include "watcher.h"
+ +
+#include "item.h" +#include "item.h"
@ -2908,20 +2906,21 @@ index 0000000..072ab86
+static const DBusObjectPathVTable snw_vtable = { .message_function = +static const DBusObjectPathVTable snw_vtable = { .message_function =
+ snw_message_handler }; + snw_message_handler };
+ +
+int +void
+watcher_start(Watcher *watcher, DBusConnection *conn, +watcher_start(Watcher *watcher, DBusConnection *conn,
+ struct wl_event_loop *loop) + struct wl_event_loop *loop)
+{ +{
+ DBusError err = DBUS_ERROR_INIT; + DBusError err = DBUS_ERROR_INIT;
+ int r; + int r, flags;
+ +
+ wl_list_init(&watcher->items); + wl_list_init(&watcher->items);
+ wl_list_init(&watcher->trays); + wl_list_init(&watcher->trays);
+ watcher->conn = conn; + watcher->conn = conn;
+ watcher->loop = loop; + watcher->loop = loop;
+ +
+ flags = DBUS_NAME_FLAG_REPLACE_EXISTING | DBUS_NAME_FLAG_DO_NOT_QUEUE;
+ r = dbus_bus_request_name(conn, SNW_NAME, + r = dbus_bus_request_name(conn, SNW_NAME,
+ DBUS_NAME_FLAG_REPLACE_EXISTING, NULL); + flags, NULL);
+ if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) + if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
+ goto fail; + goto fail;
+ +
@ -2945,13 +2944,13 @@ index 0000000..072ab86
+ goto fail; + goto fail;
+ } + }
+ +
+ dbus_error_free(&err); + watcher->running = 1;
+ return 0; + return;
+ +
+fail: +fail:
+ fprintf(stderr, "Couldn't start watcher, systray not available\n"); + fprintf(stderr, "Couldn't start watcher, systray not available\n");
+ dbus_error_free(&err); + dbus_error_free(&err);
+ return -1; + return;
+} +}
+ +
+void +void
@ -2961,6 +2960,7 @@ index 0000000..072ab86
+ dbus_bus_remove_match(watcher->conn, match_rule, NULL); + dbus_bus_remove_match(watcher->conn, match_rule, NULL);
+ dbus_connection_remove_filter(watcher->conn, filter_bus, watcher); + dbus_connection_remove_filter(watcher->conn, filter_bus, watcher);
+ dbus_bus_release_name(watcher->conn, SNW_NAME, NULL); + dbus_bus_release_name(watcher->conn, SNW_NAME, NULL);
+ watcher->running = 0;
+} +}
+ +
+int +int
@ -2979,10 +2979,10 @@ index 0000000..072ab86
+} +}
diff --git a/systray/watcher.h b/systray/watcher.h diff --git a/systray/watcher.h b/systray/watcher.h
new file mode 100644 new file mode 100644
index 0000000..0178587 index 0000000..127eb64
--- /dev/null --- /dev/null
+++ b/systray/watcher.h +++ b/systray/watcher.h
@@ -0,0 +1,34 @@ @@ -0,0 +1,35 @@
+#ifndef WATCHER_H +#ifndef WATCHER_H
+#define WATCHER_H +#define WATCHER_H
+ +
@ -3007,9 +3007,10 @@ index 0000000..0178587
+ struct wl_list trays; + struct wl_list trays;
+ struct wl_event_loop *loop; + struct wl_event_loop *loop;
+ DBusConnection *conn; + DBusConnection *conn;
+ int running;
+} Watcher; +} Watcher;
+ +
+int watcher_start (Watcher *watcher, DBusConnection *conn, +void watcher_start (Watcher *watcher, DBusConnection *conn,
+ struct wl_event_loop *loop); + struct wl_event_loop *loop);
+void watcher_stop (Watcher *watcher); +void watcher_stop (Watcher *watcher);
+ +
@ -3018,5 +3019,5 @@ index 0000000..0178587
+ +
+#endif /* WATCHER_H */ +#endif /* WATCHER_H */
-- --
2.48.1 2.49.0