From 71f7b97dca2d781668e826aae7e06544958534f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janne=20Vetel=C3=A4inen?= Date: Fri, 23 Aug 2024 18:39:17 +0300 Subject: [PATCH 1/1] Add a system tray next to the sewn's bar --- Makefile | 8 +- config.def.h | 2 + dwl.c | 222 ++++++++++++++++++++++++++++++++++++++++++++- include/.gitignore | 1 + lib/.gitignore | 1 + 5 files changed, 225 insertions(+), 9 deletions(-) create mode 100644 include/.gitignore create mode 100644 lib/.gitignore diff --git a/Makefile b/Makefile index 9bc67db..853b04c 100644 --- a/Makefile +++ b/Makefile @@ -7,14 +7,14 @@ include config.mk DWLCPPFLAGS = -I. -DWLR_USE_UNSTABLE -D_POSIX_C_SOURCE=200809L \ -DVERSION=\"$(VERSION)\" $(XWAYLAND) DWLDEVCFLAGS = -g -pedantic -Wall -Wextra -Wdeclaration-after-statement \ - -Wno-unused-parameter -Wshadow -Wunused-macros -Werror=strict-prototypes \ + -Wno-unused-parameter -Wshadow -Wunused-macros \ -Werror=implicit -Werror=return-type -Werror=incompatible-pointer-types \ -Wfloat-conversion # CFLAGS / LDFLAGS -PKGS = wlroots-0.18 wayland-server xkbcommon libinput pixman-1 fcft $(XLIBS) -DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS) -LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` -lm $(LIBS) +PKGS = wlroots-0.18 wayland-server xkbcommon libinput pixman-1 fcft $(XLIBS) gtk4 gtk4-layer-shell-0 +DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS) -Iinclude +LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` -lm $(LIBS) -Llib -lstatusnotifier-systray-gtk4 all: dwl dwl: dwl.o util.o diff --git a/config.def.h b/config.def.h index 5d1dc2b..bb9366f 100644 --- a/config.def.h +++ b/config.def.h @@ -11,6 +11,8 @@ static const int showbar = 1; /* 0 means no bar */ static const int topbar = 1; /* 0 means bottom bar */ static const char *fonts[] = {"monospace:size=10"}; static const float rootcolor[] = COLOR(0x000000ff); +static const int trayspacing = 4; /* Spacing between icons in system tray */ +static const int traymargins = 4; /* System tray inner margins */ /* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */ static const float fullscreen_bg[] = {0.1f, 0.1f, 0.1f, 1.0f}; /* You can also use glsl colors */ static uint32_t colors[][3] = { diff --git a/dwl.c b/dwl.c index ece537a..24f550a 100644 --- a/dwl.c +++ b/dwl.c @@ -72,9 +72,22 @@ #include "util.h" #include "drwl.h" +#include +#include +#include +#include +#include +#include +#include +#include + /* macros */ +#ifndef MAX #define MAX(A, B) ((A) > (B) ? (A) : (B)) +#endif /* MAX */ +#ifndef MIN #define MIN(A, B) ((A) < (B) ? (A) : (B)) +#endif /* MIN */ #define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS) #define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags])) #define LENGTH(X) (sizeof X / sizeof X[0]) @@ -324,6 +337,15 @@ static void focusstack(const Arg *arg); static Client *focustop(Monitor *m); static void fullscreennotify(struct wl_listener *listener, void *data); static void gpureset(struct wl_listener *listener, void *data); +static void gtkactivate(GtkApplication *app, void *data); +static void gtkclosewindows(void *data, void *udata); +static void gtkhandletogglebarmsg(void *data); +static void gtkhandlewidthnotify(SnSystray *systray, GParamSpec *pspec, void *data); +static void* gtkinit(void *data); +static void gtkspawnstray(Monitor *m, GtkApplication *app); +static void gtkterminate(void *data); +static void gtktoggletray(void *data, void *udata); +static GdkMonitor* gtkwlrtogdkmon(Monitor *wlrmon); static void handlesig(int signo); static void incnmaster(const Arg *arg); static void inputdevice(struct wl_listener *listener, void *data); @@ -380,7 +402,7 @@ static void unlocksession(struct wl_listener *listener, void *data); static void unmaplayersurfacenotify(struct wl_listener *listener, void *data); static void unmapnotify(struct wl_listener *listener, void *data); static void updatemons(struct wl_listener *listener, void *data); -static void updatebar(Monitor *m); +static void updatebar(Monitor *m, int traywidth); static void updatetitle(struct wl_listener *listener, void *data); static void urgent(struct wl_listener *listener, void *data); static void view(const Arg *arg); @@ -394,6 +416,8 @@ static void zoom(const Arg *arg); /* variables */ static const char broken[] = "broken"; static pid_t child_pid = -1; +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_t gtkthread; /* Gtk functions are only allowed to be called from this thread */ static int locked; static void *exclusive_focus; static struct wl_display *dpy; @@ -1187,7 +1211,7 @@ createmon(struct wl_listener *listener, void *data) m->scene_buffer = wlr_scene_buffer_create(layers[LyrBottom], NULL); m->scene_buffer->point_accepts_input = baracceptsinput; - updatebar(m); + updatebar(m, 0); wl_list_insert(&mons, &m->link); drawbars(); @@ -1518,6 +1542,8 @@ drawbar(Monitor *m) if (!(buf = bufmon(m))) return; + pthread_mutex_lock(&mutex); + /* draw status first so it can be overdrawn by tags later */ if (m == selmon) { /* status is only drawn on selected monitor */ drwl_setscheme(m->drw, colors[SchemeNorm]); @@ -1566,6 +1592,7 @@ drawbar(Monitor *m) m->m.y + (topbar ? 0 : m->m.height - m->b.real_height)); wlr_scene_buffer_set_buffer(m->scene_buffer, &buf->base); wlr_buffer_unlock(&buf->base); + pthread_mutex_unlock(&mutex); } void @@ -1710,6 +1737,174 @@ fullscreennotify(struct wl_listener *listener, void *data) setfullscreen(c, client_wants_fullscreen(c)); } +void +gtkactivate(GtkApplication *app, void *data) +{ + GdkDisplay *display; + GtkCssProvider *cssp; + char csss[64]; + Monitor *m; + uint32_t bgcolor; + + bgcolor = colors[SchemeNorm][1] >> 8; + display = gdk_display_get_default(); + cssp = gtk_css_provider_new(); + sprintf(csss, "window{background-color:#%06x;}", bgcolor); + gtk_css_provider_load_from_string(cssp, csss); + gtk_style_context_add_provider_for_display(display, + GTK_STYLE_PROVIDER(cssp), + GTK_STYLE_PROVIDER_PRIORITY_USER); + + wl_list_for_each(m, &mons, link) + gtkspawnstray(m, app); + + g_object_unref(cssp); +} + +void +gtkclosewindows(void *data, void *udata) +{ + GtkWindow *window = GTK_WINDOW(data); + + gtk_window_close(window); +} + +void +gtkhandletogglebarmsg(void *data) +{ + GtkApplication *app; + GList *windows; + + app = GTK_APPLICATION(g_application_get_default()); + windows = gtk_application_get_windows(app); + g_list_foreach(windows, gtktoggletray, data); +} + +void +gtkhandlewidthnotify(SnSystray *systray, GParamSpec *pspec, void *data) +{ + Monitor *m = (Monitor *)data; + int traywidth; + + traywidth = sn_systray_get_width(systray); + + updatebar(m, traywidth); + drawbar(m); +} + +void* +gtkinit(void *data) +{ + GtkApplication *app = gtk_application_new("org.dwl.systray", + G_APPLICATION_NON_UNIQUE); + g_signal_connect(app, "activate", G_CALLBACK(gtkactivate), NULL); + g_application_run(G_APPLICATION(app), 0, NULL); + + g_object_unref(app); + + return NULL; +} + +void +gtkspawnstray(Monitor *m, GtkApplication *app) +{ + GdkMonitor *gdkmon; + GtkWindow *window; + SnSystray *systray; + const char *conn; + gboolean anchors[4]; + int iconsize, tray_init_width, tray_height; + + gdkmon = gtkwlrtogdkmon(m); + if (gdkmon == NULL) + die("Failed to get gdkmon"); + + conn = gdk_monitor_get_connector(gdkmon); + iconsize = m->b.real_height - 2 * traymargins; + tray_height = m->b.real_height; + tray_init_width = m->b.real_height; + + if (topbar) { + anchors[GTK_LAYER_SHELL_EDGE_LEFT] = false; + anchors[GTK_LAYER_SHELL_EDGE_RIGHT] = true; + anchors[GTK_LAYER_SHELL_EDGE_TOP] = true; + anchors[GTK_LAYER_SHELL_EDGE_BOTTOM] = false; + } else { + anchors[GTK_LAYER_SHELL_EDGE_LEFT] = false; + anchors[GTK_LAYER_SHELL_EDGE_RIGHT] = true; + anchors[GTK_LAYER_SHELL_EDGE_TOP] = false; + anchors[GTK_LAYER_SHELL_EDGE_BOTTOM] = true; + } + + systray = sn_systray_new(iconsize, + traymargins, + trayspacing, + conn); + window = GTK_WINDOW(gtk_window_new()); + + gtk_window_set_default_size(window, tray_init_width, tray_height); + gtk_window_set_child(window, GTK_WIDGET(systray)); + gtk_window_set_application(window, app); + gtk_layer_init_for_window(window); + gtk_layer_set_layer(window, GTK_LAYER_SHELL_LAYER_BOTTOM); + gtk_layer_set_exclusive_zone(window, -1); + gtk_layer_set_monitor(window, gdkmon); + + for (int j = 0; j < GTK_LAYER_SHELL_EDGE_ENTRY_NUMBER; j++) { + gtk_layer_set_anchor(window, j, anchors[j]); + } + + updatebar(m, tray_init_width); + g_signal_connect(systray, "notify::curwidth", G_CALLBACK(gtkhandlewidthnotify), m); + gtk_window_present(window); +} + +void +gtkterminate(void *data) +{ + GtkApplication *app; + GList *windows; + + app = GTK_APPLICATION(g_application_get_default()); + windows = gtk_application_get_windows(app); + g_list_foreach(windows, gtkclosewindows, NULL); +} + +GdkMonitor* +gtkwlrtogdkmon(Monitor *wlrmon) +{ + GdkMonitor *gdkmon = NULL; + + GListModel *gdkmons; + GdkDisplay *display; + const char *gdkname; + const char *wlrname; + unsigned int i; + + wlrname = wlrmon->wlr_output->name; + display = gdk_display_get_default(); + gdkmons = gdk_display_get_monitors(display); + + for (i = 0; i < g_list_model_get_n_items(gdkmons); i++) { + GdkMonitor *mon = g_list_model_get_item(gdkmons, i); + gdkname = gdk_monitor_get_connector(mon); + if (strcmp(wlrname, gdkname) == 0) + gdkmon = mon; + } + + return gdkmon; +} + +void +gtktoggletray(void *data, void *udata) +{ + GtkWidget *widget = GTK_WIDGET(data); + int *pbarvisible = (int *)udata; + int barvisible = GPOINTER_TO_INT(pbarvisible); + + gtk_widget_set_visible(widget, barvisible); +} + void gpureset(struct wl_listener *listener, void *data) { @@ -2293,6 +2488,8 @@ powermgrsetmode(struct wl_listener *listener, void *data) void quit(const Arg *arg) { + g_idle_add_once(gtkterminate, NULL); + pthread_join(gtkthread, NULL); wl_display_terminate(dpy); } @@ -2836,6 +3033,9 @@ setup(void) fprintf(stderr, "failed to setup XWayland X server, continuing without it\n"); } #endif + + // Gtk functions are only allowed to be called from this thread. + pthread_create(>kthread, NULL, >kinit, NULL); } void @@ -2943,9 +3143,21 @@ tile(Monitor *m) void togglebar(const Arg *arg) { + int barvisible; + int *pbarvisible; + wlr_scene_node_set_enabled(&selmon->scene_buffer->node, !selmon->scene_buffer->node.enabled); arrangelayers(selmon); + + // Notify gtkthread + if (selmon->scene_buffer->node.enabled) + barvisible = 1; + else + barvisible = 0; + + pbarvisible = GINT_TO_POINTER(barvisible); + g_idle_add_once(gtkhandletogglebarmsg, pbarvisible); } void @@ -3140,7 +3352,7 @@ updatemons(struct wl_listener *listener, void *data) if (stext[0] == '\0') strncpy(stext, "dwl-"VERSION, sizeof(stext)); wl_list_for_each(m, &mons, link) { - updatebar(m); + updatebar(m, 0); drawbar(m); } @@ -3155,7 +3367,7 @@ updatemons(struct wl_listener *listener, void *data) } void -updatebar(Monitor *m) +updatebar(Monitor *m, int traywidth) { size_t i; int rw, rh; @@ -3163,7 +3375,7 @@ updatebar(Monitor *m) wlr_output_transformed_resolution(m->wlr_output, &rw, &rh); m->b.width = rw; - m->b.real_width = (int)((float)m->b.width / m->wlr_output->scale); + m->b.real_width = (int)((float)m->b.width / m->wlr_output->scale) - traywidth; wlr_scene_node_set_enabled(&m->scene_buffer->node, m->wlr_output->enabled ? showbar : 0); diff --git a/include/.gitignore b/include/.gitignore new file mode 100644 index 0000000..424c745 --- /dev/null +++ b/include/.gitignore @@ -0,0 +1 @@ +*.h diff --git a/lib/.gitignore b/lib/.gitignore new file mode 100644 index 0000000..10301e2 --- /dev/null +++ b/lib/.gitignore @@ -0,0 +1 @@ +*.a -- 2.46.0