mirror of
				https://codeberg.org/dwl/dwl-patches.git
				synced 2025-10-31 03:54:19 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			399 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			399 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From 71f7b97dca2d781668e826aae7e06544958534f6 Mon Sep 17 00:00:00 2001
 | |
| From: =?UTF-8?q?Janne=20Vetel=C3=A4inen?= <janne.vetelainen@elisanet.fi>
 | |
| 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 <snsystray.h>
 | |
| +#include <gdk/gdk.h>
 | |
| +#include <gio/gio.h>
 | |
| +#include <glib-object.h>
 | |
| +#include <glib.h>
 | |
| +#include <gtk/gtk.h>
 | |
| +#include <gtk4-layer-shell.h>
 | |
| +#include <pthread.h>
 | |
| +
 | |
|  /* 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
 | |
| 
 | 
