nixos-dotfiles

nixos-dotfiles

https://git.tonybtw.com/nixos-dotfiles.git git://git.tonybtw.com/nixos-dotfiles.git
39,110 bytes raw
1
From 8906a73dbc8996dd1bfff15f5b26aaee6d45fd61 Mon Sep 17 00:00:00 2001
2
From: sewn <sewn@disroot.org>
3
Date: Tue, 29 Jul 2025 15:21:19 +0300
4
Subject: [PATCH] Implement dwm bar clone
5
6
Signed-off-by: sewn <sewn@disroot.org>
7
---
8
 Makefile     |   2 +-
9
 config.def.h |  33 ++--
10
 drwl.h       | 311 ++++++++++++++++++++++++++++++++++++
11
 dwl.c        | 441 +++++++++++++++++++++++++++++++++++++++++----------
12
 4 files changed, 695 insertions(+), 92 deletions(-)
13
 create mode 100644 drwl.h
14
15
diff --git a/Makefile b/Makefile
16
index 578194f..279b1c0 100644
17
--- a/Makefile
18
+++ b/Makefile
19
@@ -12,7 +12,7 @@ DWLDEVCFLAGS = -g -Wpedantic -Wall -Wextra -Wdeclaration-after-statement \
20
 	-Wfloat-conversion
21
 
22
 # CFLAGS / LDFLAGS
23
-PKGS      = wayland-server xkbcommon libinput $(XLIBS)
24
+PKGS      = wayland-server xkbcommon libinput pixman-1 fcft $(XLIBS)
25
 DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(WLR_INCS) $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS)
26
 LDLIBS    = `$(PKG_CONFIG) --libs $(PKGS)` $(WLR_LIBS) -lm $(LIBS)
27
 
28
diff --git a/config.def.h b/config.def.h
29
index 95c2afa..1b7472d 100644
30
--- a/config.def.h
31
+++ b/config.def.h
32
@@ -7,15 +7,21 @@
33
 static const int sloppyfocus               = 1;  /* focus follows mouse */
34
 static const int bypass_surface_visibility = 0;  /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible  */
35
 static const unsigned int borderpx         = 1;  /* border pixel of windows */
36
-static const float rootcolor[]             = COLOR(0x222222ff);
37
-static const float bordercolor[]           = COLOR(0x444444ff);
38
-static const float focuscolor[]            = COLOR(0x005577ff);
39
-static const float urgentcolor[]           = COLOR(0xff0000ff);
40
+static const int showbar                   = 1; /* 0 means no bar */
41
+static const int topbar                    = 1; /* 0 means bottom bar */
42
+static const char *fonts[]                 = {"monospace:size=10"};
43
+static const float rootcolor[]             = COLOR(0x000000ff);
44
 /* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */
45
 static const float fullscreen_bg[]         = {0.0f, 0.0f, 0.0f, 1.0f}; /* You can also use glsl colors */
46
+static uint32_t colors[][3]                = {
47
+	/*               fg          bg          border    */
48
+	[SchemeNorm] = { 0xbbbbbbff, 0x222222ff, 0x444444ff },
49
+	[SchemeSel]  = { 0xeeeeeeff, 0x005577ff, 0x005577ff },
50
+	[SchemeUrg]  = { 0,          0,          0x770000ff },
51
+};
52
 
53
-/* tagging - TAGCOUNT must be no greater than 31 */
54
-#define TAGCOUNT (9)
55
+/* tagging */
56
+static char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
57
 
58
 /* logging */
59
 static int log_level = WLR_ERROR;
60
@@ -127,6 +133,7 @@ static const Key keys[] = {
61
 	/* modifier                  key                 function        argument */
62
 	{ MODKEY,                    XKB_KEY_p,          spawn,          {.v = menucmd} },
63
 	{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return,     spawn,          {.v = termcmd} },
64
+	{ MODKEY,                    XKB_KEY_b,          togglebar,      {0} },
65
 	{ MODKEY,                    XKB_KEY_j,          focusstack,     {.i = +1} },
66
 	{ MODKEY,                    XKB_KEY_k,          focusstack,     {.i = -1} },
67
 	{ MODKEY,                    XKB_KEY_i,          incnmaster,     {.i = +1} },
68
@@ -170,7 +177,15 @@ static const Key keys[] = {
69
 };
70
 
71
 static const Button buttons[] = {
72
-	{ MODKEY, BTN_LEFT,   moveresize,     {.ui = CurMove} },
73
-	{ MODKEY, BTN_MIDDLE, togglefloating, {0} },
74
-	{ MODKEY, BTN_RIGHT,  moveresize,     {.ui = CurResize} },
75
+	{ ClkLtSymbol, 0,      BTN_LEFT,   setlayout,      {.v = &layouts[0]} },
76
+	{ ClkLtSymbol, 0,      BTN_RIGHT,  setlayout,      {.v = &layouts[2]} },
77
+	{ ClkTitle,    0,      BTN_MIDDLE, zoom,           {0} },
78
+	{ ClkStatus,   0,      BTN_MIDDLE, spawn,          {.v = termcmd} },
79
+	{ ClkClient,   MODKEY, BTN_LEFT,   moveresize,     {.ui = CurMove} },
80
+	{ ClkClient,   MODKEY, BTN_MIDDLE, togglefloating, {0} },
81
+	{ ClkClient,   MODKEY, BTN_RIGHT,  moveresize,     {.ui = CurResize} },
82
+	{ ClkTagBar,   0,      BTN_LEFT,   view,           {0} },
83
+	{ ClkTagBar,   0,      BTN_RIGHT,  toggleview,     {0} },
84
+	{ ClkTagBar,   MODKEY, BTN_LEFT,   tag,            {0} },
85
+	{ ClkTagBar,   MODKEY, BTN_RIGHT,  toggletag,      {0} },
86
 };
87
diff --git a/drwl.h b/drwl.h
88
new file mode 100644
89
index 0000000..90cc35b
90
--- /dev/null
91
+++ b/drwl.h
92
@@ -0,0 +1,311 @@
93
+/*
94
+ * drwl - https://codeberg.org/sewn/drwl
95
+ *
96
+ * Copyright (c) 2023-2025 sewn <sewn@disroot.org>
97
+ * Copyright (c) 2024 notchoc <notchoc@disroot.org>
98
+ * 
99
+ * Permission is hereby granted, free of charge, to any person obtaining
100
+ * a copy of this software and associated documentation files (the
101
+ * "Software"), to deal in the Software without restriction, including
102
+ * without limitation the rights to use, copy, modify, merge, publish,
103
+ * distribute, sublicense, and/or sell copies of the Software, and to
104
+ * permit persons to whom the Software is furnished to do so, subject to
105
+ * the following conditions:
106
+ * 
107
+ * The above copyright notice and this permission notice shall be
108
+ * included in all copies or substantial portions of the Software.
109
+ * 
110
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
111
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
112
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
113
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
114
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
115
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
116
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
117
+ *
118
+ * The UTF-8 Decoder included is from Bjoern Hoehrmann:
119
+ * Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
120
+ * See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
121
+ */
122
+#pragma once
123
+
124
+#include <stdlib.h>
125
+#include <fcft/fcft.h>
126
+#include <pixman-1/pixman.h>
127
+
128
+enum { ColFg, ColBg, ColBorder }; /* colorscheme index */
129
+
130
+typedef struct fcft_font Fnt;
131
+typedef pixman_image_t Img;
132
+
133
+typedef struct {
134
+	Img *image;
135
+	Fnt *font;
136
+	uint32_t *scheme;
137
+} Drwl;
138
+
139
+#define UTF8_ACCEPT 0
140
+#define UTF8_REJECT 12
141
+#define UTF8_INVALID 0xFFFD
142
+
143
+static const uint8_t utf8d[] = {
144
+	 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
145
+	 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
146
+	 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
147
+	 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
148
+	 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,  9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
149
+	 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
150
+	 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
151
+	10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
152
+
153
+	 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
154
+	12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
155
+	12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
156
+	12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
157
+	12,36,12,12,12,12,12,12,12,12,12,12,
158
+};
159
+
160
+static inline uint32_t
161
+utf8decode(uint32_t *state, uint32_t *codep, uint8_t byte)
162
+{
163
+	uint32_t type = utf8d[byte];
164
+
165
+	*codep = (*state != UTF8_ACCEPT) ?
166
+		(byte & 0x3fu) | (*codep << 6) :
167
+		(0xff >> type) & (byte);
168
+
169
+	*state = utf8d[256 + *state + type];
170
+	return *state;
171
+}
172
+
173
+static int
174
+drwl_init(void)
175
+{
176
+	fcft_set_scaling_filter(FCFT_SCALING_FILTER_LANCZOS3);
177
+	return fcft_init(FCFT_LOG_COLORIZE_AUTO, 0, FCFT_LOG_CLASS_ERROR);
178
+}
179
+
180
+static Drwl *
181
+drwl_create(void)
182
+{
183
+	Drwl *drwl;
184
+	
185
+	if (!(drwl = calloc(1, sizeof(Drwl))))
186
+		return NULL;
187
+
188
+	return drwl;
189
+}
190
+
191
+static void
192
+drwl_setfont(Drwl *drwl, Fnt *font)
193
+{
194
+	if (drwl)
195
+		drwl->font = font;
196
+}
197
+
198
+static void
199
+drwl_setimage(Drwl *drwl, Img *image)
200
+{
201
+	if (drwl)
202
+		drwl->image = image;
203
+}
204
+
205
+static Fnt *
206
+drwl_font_create(Drwl *drwl, size_t count,
207
+		const char *names[static count], const char *attributes)
208
+{
209
+	Fnt *font = fcft_from_name(count, names, attributes);
210
+	if (drwl)
211
+		drwl_setfont(drwl, font);
212
+	return font;
213
+}
214
+
215
+static void
216
+drwl_font_destroy(Fnt *font)
217
+{
218
+	fcft_destroy(font);
219
+}
220
+
221
+static inline pixman_color_t
222
+convert_color(uint32_t clr)
223
+{
224
+	return (pixman_color_t){
225
+		((clr >> 24) & 0xFF) * 0x101 * (clr & 0xFF) / 0xFF,
226
+		((clr >> 16) & 0xFF) * 0x101 * (clr & 0xFF) / 0xFF,
227
+		((clr >> 8) & 0xFF) * 0x101 * (clr & 0xFF) / 0xFF,
228
+		(clr & 0xFF) * 0x101
229
+	};
230
+}
231
+
232
+static void
233
+drwl_setscheme(Drwl *drwl, uint32_t *scm)
234
+{
235
+	if (drwl)
236
+		drwl->scheme = scm;
237
+}
238
+
239
+static Img *
240
+drwl_image_create(Drwl *drwl, unsigned int w, unsigned int h, uint32_t *bits)
241
+{
242
+	Img *image;
243
+	pixman_region32_t clip;
244
+
245
+	image = pixman_image_create_bits_no_clear(
246
+		PIXMAN_a8r8g8b8, w, h, bits, w * 4);
247
+	if (!image)
248
+		return NULL;
249
+	pixman_region32_init_rect(&clip, 0, 0, w, h);
250
+	pixman_image_set_clip_region32(image, &clip);
251
+	pixman_region32_fini(&clip);
252
+
253
+	if (drwl)
254
+		drwl_setimage(drwl, image);
255
+	return image;
256
+}
257
+
258
+static void
259
+drwl_rect(Drwl *drwl,
260
+		int x, int y, unsigned int w, unsigned int h,
261
+		int filled, int invert)
262
+{
263
+	pixman_color_t clr;
264
+	if (!drwl || !drwl->scheme || !drwl->image)
265
+		return;
266
+
267
+	clr = convert_color(drwl->scheme[invert ? ColBg : ColFg]);
268
+	if (filled)
269
+		pixman_image_fill_rectangles(PIXMAN_OP_SRC, drwl->image, &clr, 1,
270
+			&(pixman_rectangle16_t){x, y, w, h});
271
+	else
272
+		pixman_image_fill_rectangles(PIXMAN_OP_SRC, drwl->image, &clr, 4,
273
+			(pixman_rectangle16_t[4]){
274
+				{ x,         y,         w, 1 },
275
+				{ x,         y + h - 1, w, 1 },
276
+				{ x,         y,         1, h },
277
+				{ x + w - 1, y,         1, h }});
278
+}
279
+
280
+static int
281
+drwl_text(Drwl *drwl,
282
+		int x, int y, unsigned int w, unsigned int h,
283
+		unsigned int lpad, const char *text, int invert)
284
+{
285
+	int ty;
286
+	int render = x || y || w || h;
287
+	long x_kern;
288
+	uint32_t cp = 0, last_cp = 0, state;
289
+	pixman_color_t clr;
290
+	pixman_image_t *fg_pix = NULL;
291
+	int noellipsis = 0;
292
+	const struct fcft_glyph *glyph, *eg = NULL;
293
+	int fcft_subpixel_mode = FCFT_SUBPIXEL_DEFAULT;
294
+
295
+	if (!drwl || (render && (!drwl->scheme || !w || !drwl->image)) || !text || !drwl->font)
296
+		return 0;
297
+
298
+	if (!render) {
299
+		w = invert ? invert : ~invert;
300
+	} else {
301
+		clr = convert_color(drwl->scheme[invert ? ColBg : ColFg]);
302
+		fg_pix = pixman_image_create_solid_fill(&clr);
303
+
304
+		drwl_rect(drwl, x, y, w, h, 1, !invert);
305
+
306
+		x += lpad;
307
+		w -= lpad;
308
+	}
309
+
310
+	if (render && (drwl->scheme[ColBg] & 0xFF) != 0xFF)
311
+		fcft_subpixel_mode = FCFT_SUBPIXEL_NONE;
312
+
313
+	if (render)
314
+		eg = fcft_rasterize_char_utf32(drwl->font, 0x2026 /* … */, fcft_subpixel_mode);
315
+
316
+	for (const char *p = text, *pp; pp = p, *p; p++) {
317
+		for (state = UTF8_ACCEPT; *p &&
318
+		     utf8decode(&state, &cp, *p) > UTF8_REJECT; p++)
319
+			;
320
+		if (!*p || state == UTF8_REJECT) {
321
+			cp = UTF8_INVALID;
322
+			if (p > pp)
323
+				p--;
324
+		}
325
+
326
+		glyph = fcft_rasterize_char_utf32(drwl->font, cp, fcft_subpixel_mode);
327
+		if (!glyph)
328
+			continue;
329
+
330
+		x_kern = 0;
331
+		if (last_cp)
332
+			fcft_kerning(drwl->font, last_cp, cp, &x_kern, NULL);
333
+		last_cp = cp;
334
+
335
+		ty = y + (h - drwl->font->height) / 2 + drwl->font->ascent;
336
+
337
+		if (render && !noellipsis && x_kern + glyph->advance.x + eg->advance.x > w &&
338
+		    *(p + 1) != '\0') {
339
+			/* cannot fit ellipsis after current codepoint */
340
+			if (drwl_text(drwl, 0, 0, 0, 0, 0, pp, 0) + x_kern <= w) {
341
+				noellipsis = 1;
342
+			} else {
343
+				w -= eg->advance.x;
344
+				pixman_image_composite32(
345
+					PIXMAN_OP_OVER, fg_pix, eg->pix, drwl->image, 0, 0, 0, 0,
346
+					x + eg->x, ty - eg->y, eg->width, eg->height);
347
+			}
348
+		}
349
+
350
+		if ((x_kern + glyph->advance.x) > w)
351
+			break;
352
+
353
+		x += x_kern;
354
+
355
+		if (render && pixman_image_get_format(glyph->pix) == PIXMAN_a8r8g8b8)
356
+			/* pre-rendered glyphs (eg. emoji) */
357
+			pixman_image_composite32(
358
+				PIXMAN_OP_OVER, glyph->pix, NULL, drwl->image, 0, 0, 0, 0,
359
+				x + glyph->x, ty - glyph->y, glyph->width, glyph->height);
360
+		else if (render)
361
+			pixman_image_composite32(
362
+				PIXMAN_OP_OVER, fg_pix, glyph->pix, drwl->image, 0, 0, 0, 0,
363
+				x + glyph->x, ty - glyph->y, glyph->width, glyph->height);
364
+
365
+		x += glyph->advance.x;
366
+		w -= glyph->advance.x;
367
+	}
368
+
369
+	if (render)
370
+		pixman_image_unref(fg_pix);
371
+
372
+	return x + (render ? w : 0);
373
+}
374
+
375
+static unsigned int
376
+drwl_font_getwidth(Drwl *drwl, const char *text)
377
+{
378
+	if (!drwl || !drwl->font || !text)
379
+		return 0;
380
+	return drwl_text(drwl, 0, 0, 0, 0, 0, text, 0);
381
+}
382
+
383
+static void
384
+drwl_image_destroy(Img *image)
385
+{
386
+	pixman_image_unref(image);
387
+}
388
+
389
+static void
390
+drwl_destroy(Drwl *drwl)
391
+{
392
+	if (drwl->font)
393
+		drwl_font_destroy(drwl->font);
394
+	if (drwl->image)
395
+		drwl_image_destroy(drwl->image);
396
+	free(drwl);
397
+}
398
+
399
+static void
400
+drwl_fini(void)
401
+{
402
+	fcft_fini();
403
+}
404
diff --git a/dwl.c b/dwl.c
405
index 12f441e..bf340d8 100644
406
--- a/dwl.c
407
+++ b/dwl.c
408
@@ -5,6 +5,7 @@
409
 #include <libinput.h>
410
 #include <linux/input-event-codes.h>
411
 #include <math.h>
412
+#include <libdrm/drm_fourcc.h>
413
 #include <signal.h>
414
 #include <stdio.h>
415
 #include <stdlib.h>
416
@@ -59,6 +60,7 @@
417
 #include <wlr/types/wlr_xdg_decoration_v1.h>
418
 #include <wlr/types/wlr_xdg_output_v1.h>
419
 #include <wlr/types/wlr_xdg_shell.h>
420
+#include <wlr/interfaces/wlr_buffer.h>
421
 #include <wlr/util/log.h>
422
 #include <wlr/util/region.h>
423
 #include <xkbcommon/xkbcommon.h>
424
@@ -69,6 +71,7 @@
425
 #endif
426
 
427
 #include "util.h"
428
+#include "drwl.h"
429
 
430
 /* macros */
431
 #define MAX(A, B)               ((A) > (B) ? (A) : (B))
432
@@ -77,14 +80,17 @@
433
 #define VISIBLEON(C, M)         ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags]))
434
 #define LENGTH(X)               (sizeof X / sizeof X[0])
435
 #define END(A)                  ((A) + LENGTH(A))
436
-#define TAGMASK                 ((1u << TAGCOUNT) - 1)
437
+#define TAGMASK                 ((1u << LENGTH(tags)) - 1)
438
 #define LISTEN(E, L, H)         wl_signal_add((E), ((L)->notify = (H), (L)))
439
 #define LISTEN_STATIC(E, H)     do { struct wl_listener *_l = ecalloc(1, sizeof(*_l)); _l->notify = (H); wl_signal_add((E), _l); } while (0)
440
+#define TEXTW(mon, text)        (drwl_font_getwidth(mon->drw, text) + mon->lrpad)
441
 
442
 /* enums */
443
+enum { SchemeNorm, SchemeSel, SchemeUrg }; /* color schemes */
444
 enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */
445
 enum { XDGShell, LayerShell, X11 }; /* client types */
446
 enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrTop, LyrFS, LyrOverlay, LyrBlock, NUM_LAYERS }; /* scene layers */
447
+enum { ClkTagBar, ClkLtSymbol, ClkStatus, ClkTitle, ClkClient, ClkRoot }; /* clicks */
448
 
449
 typedef union {
450
 	int i;
451
@@ -94,6 +100,7 @@ typedef union {
452
 } Arg;
453
 
454
 typedef struct {
455
+	unsigned int click;
456
 	unsigned int mod;
457
 	unsigned int button;
458
 	void (*func)(const Arg *);
459
@@ -183,10 +190,19 @@ typedef struct {
460
 	void (*arrange)(Monitor *);
461
 } Layout;
462
 
463
+typedef struct {
464
+	struct wlr_buffer base;
465
+	struct wl_listener release;
466
+	bool busy;
467
+	Img *image;
468
+	uint32_t data[];
469
+} Buffer;
470
+
471
 struct Monitor {
472
 	struct wl_list link;
473
 	struct wlr_output *wlr_output;
474
 	struct wlr_scene_output *scene_output;
475
+	struct wlr_scene_buffer *scene_buffer; /* bar buffer */
476
 	struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */
477
 	struct wl_listener frame;
478
 	struct wl_listener destroy;
479
@@ -194,6 +210,11 @@ struct Monitor {
480
 	struct wl_listener destroy_lock_surface;
481
 	struct wlr_session_lock_surface_v1 *lock_surface;
482
 	struct wlr_box m; /* monitor area, layout-relative */
483
+	struct {
484
+		int width, height;
485
+		int real_width, real_height; /* non-scaled */
486
+		float scale;
487
+	} b; /* bar area */
488
 	struct wlr_box w; /* window area, layout-relative */
489
 	struct wl_list layers[4]; /* LayerSurface.link */
490
 	const Layout *lt[2];
491
@@ -205,6 +226,9 @@ struct Monitor {
492
 	int nmaster;
493
 	char ltsymbol[16];
494
 	int asleep;
495
+	Drwl *drw;
496
+	Buffer *pool[2];
497
+	int lrpad;
498
 };
499
 
500
 typedef struct {
501
@@ -247,6 +271,13 @@ static void arrangelayer(Monitor *m, struct wl_list *list,
502
 		struct wlr_box *usable_area, int exclusive);
503
 static void arrangelayers(Monitor *m);
504
 static void axisnotify(struct wl_listener *listener, void *data);
505
+static bool baracceptsinput(struct wlr_scene_buffer *buffer, double *sx, double *sy);
506
+static void bufdestroy(struct wlr_buffer *buffer);
507
+static bool bufdatabegin(struct wlr_buffer *buffer, uint32_t flags,
508
+		void **data, uint32_t *format, size_t *stride);
509
+static void bufdataend(struct wlr_buffer *buffer);
510
+static Buffer *bufmon(Monitor *m);
511
+static void bufrelease(struct wl_listener *listener, void *data);
512
 static void buttonpress(struct wl_listener *listener, void *data);
513
 static void chvt(const Arg *arg);
514
 static void checkidleinhibitor(struct wlr_surface *exclude);
515
@@ -282,6 +313,8 @@ static void destroypointerconstraint(struct wl_listener *listener, void *data);
516
 static void destroysessionlock(struct wl_listener *listener, void *data);
517
 static void destroykeyboardgroup(struct wl_listener *listener, void *data);
518
 static Monitor *dirtomon(enum wlr_direction dir);
519
+static void drawbar(Monitor *m);
520
+static void drawbars(void);
521
 static void focusclient(Client *c, int lift);
522
 static void focusmon(const Arg *arg);
523
 static void focusstack(const Arg *arg);
524
@@ -310,7 +343,6 @@ static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int
525
 static void outputmgrtest(struct wl_listener *listener, void *data);
526
 static void pointerfocus(Client *c, struct wlr_surface *surface,
527
 		double sx, double sy, uint32_t time);
528
-static void printstatus(void);
529
 static void powermgrsetmode(struct wl_listener *listener, void *data);
530
 static void quit(const Arg *arg);
531
 static void rendermon(struct wl_listener *listener, void *data);
532
@@ -331,9 +363,11 @@ static void setsel(struct wl_listener *listener, void *data);
533
 static void setup(void);
534
 static void spawn(const Arg *arg);
535
 static void startdrag(struct wl_listener *listener, void *data);
536
+static int statusin(int fd, unsigned int mask, void *data);
537
 static void tag(const Arg *arg);
538
 static void tagmon(const Arg *arg);
539
 static void tile(Monitor *m);
540
+static void togglebar(const Arg *arg);
541
 static void togglefloating(const Arg *arg);
542
 static void togglefullscreen(const Arg *arg);
543
 static void toggletag(const Arg *arg);
544
@@ -342,6 +376,7 @@ static void unlocksession(struct wl_listener *listener, void *data);
545
 static void unmaplayersurfacenotify(struct wl_listener *listener, void *data);
546
 static void unmapnotify(struct wl_listener *listener, void *data);
547
 static void updatemons(struct wl_listener *listener, void *data);
548
+static void updatebar(Monitor *m);
549
 static void updatetitle(struct wl_listener *listener, void *data);
550
 static void urgent(struct wl_listener *listener, void *data);
551
 static void view(const Arg *arg);
552
@@ -406,6 +441,15 @@ static struct wlr_box sgeom;
553
 static struct wl_list mons;
554
 static Monitor *selmon;
555
 
556
+static char stext[256];
557
+static struct wl_event_source *status_event_source;
558
+
559
+static const struct wlr_buffer_impl buffer_impl = {
560
+    .destroy = bufdestroy,
561
+    .begin_data_ptr_access = bufdatabegin,
562
+    .end_data_ptr_access = bufdataend,
563
+};
564
+
565
 /* global event handlers */
566
 static struct wl_listener cursor_axis = {.notify = axisnotify};
567
 static struct wl_listener cursor_button = {.notify = buttonpress};
568
@@ -521,7 +565,7 @@ arrange(Monitor *m)
569
 	wlr_scene_node_set_enabled(&m->fullscreen_bg->node,
570
 			(c = focustop(m)) && c->isfullscreen);
571
 
572
-	strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol));
573
+	strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof(m->ltsymbol));
574
 
575
 	/* We move all clients (except fullscreen and unmanaged) to LyrTile while
576
 	 * in floating layout to avoid "real" floating clients be always on top */
577
@@ -576,6 +620,11 @@ arrangelayers(Monitor *m)
578
 	if (!m->wlr_output->enabled)
579
 		return;
580
 
581
+	if (m->scene_buffer->node.enabled) {
582
+		usable_area.height -= m->b.real_height;
583
+		usable_area.y += topbar ? m->b.real_height : 0;
584
+	}
585
+
586
 	/* Arrange exclusive surfaces from top->bottom */
587
 	for (i = 3; i >= 0; i--)
588
 		arrangelayer(m, &m->layers[i], &usable_area, 1);
589
@@ -618,17 +667,102 @@ axisnotify(struct wl_listener *listener, void *data)
590
 			event->delta_discrete, event->source, event->relative_direction);
591
 }
592
 
593
+bool
594
+baracceptsinput(struct wlr_scene_buffer *buffer, double *sx, double *sy)
595
+{
596
+	return true;
597
+}
598
+
599
+void
600
+bufdestroy(struct wlr_buffer *wlr_buffer)
601
+{
602
+	Buffer *buf = wl_container_of(wlr_buffer, buf, base);
603
+	if (buf->busy)
604
+		wl_list_remove(&buf->release.link);
605
+	drwl_image_destroy(buf->image);
606
+	free(buf);
607
+}
608
+
609
+bool
610
+bufdatabegin(struct wlr_buffer *wlr_buffer, uint32_t flags,
611
+		void **data, uint32_t *format, size_t *stride)
612
+{
613
+	Buffer *buf = wl_container_of(wlr_buffer, buf, base);
614
+
615
+	if (flags & WLR_BUFFER_DATA_PTR_ACCESS_WRITE) return false;
616
+
617
+	*data   = buf->data;
618
+	*stride = wlr_buffer->width * 4;
619
+	*format = DRM_FORMAT_ARGB8888;
620
+
621
+	return true;
622
+}
623
+
624
+void
625
+bufdataend(struct wlr_buffer *wlr_buffer)
626
+{
627
+}
628
+
629
+Buffer *
630
+bufmon(Monitor *m)
631
+{
632
+	size_t i;
633
+	Buffer *buf = NULL;
634
+
635
+	for (i = 0; i < LENGTH(m->pool); i++) {
636
+		if (m->pool[i]) {
637
+			if (m->pool[i]->busy)
638
+				continue;
639
+			buf = m->pool[i];
640
+			break;
641
+		}
642
+
643
+		buf = ecalloc(1, sizeof(Buffer) + (m->b.width * 4 * m->b.height));
644
+		buf->image = drwl_image_create(NULL, m->b.width, m->b.height, buf->data);
645
+		wlr_buffer_init(&buf->base, &buffer_impl, m->b.width, m->b.height);
646
+		m->pool[i] = buf;
647
+		break;
648
+	}
649
+	if (!buf)
650
+		return NULL;
651
+
652
+	buf->busy = true;
653
+	LISTEN(&buf->base.events.release, &buf->release, bufrelease);
654
+	wlr_buffer_lock(&buf->base);
655
+	drwl_setimage(m->drw, buf->image);
656
+	return buf;
657
+}
658
+
659
+void
660
+bufrelease(struct wl_listener *listener, void *data)
661
+{
662
+	Buffer *buf = wl_container_of(listener, buf, release);
663
+	buf->busy = false;
664
+	wl_list_remove(&buf->release.link);
665
+}
666
+
667
 void
668
 buttonpress(struct wl_listener *listener, void *data)
669
 {
670
+	unsigned int i = 0, x = 0;
671
+	double cx;
672
+	unsigned int click;
673
 	struct wlr_pointer_button_event *event = data;
674
 	struct wlr_keyboard *keyboard;
675
+	struct wlr_scene_node *node;
676
+	struct wlr_scene_buffer *buffer;
677
 	uint32_t mods;
678
+	Arg arg = {0};
679
 	Client *c;
680
 	const Button *b;
681
 
682
 	wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
683
 
684
+	click = ClkRoot;
685
+	xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL);
686
+	if (c)
687
+		click = ClkClient;
688
+
689
 	switch (event->state) {
690
 	case WL_POINTER_BUTTON_STATE_PRESSED:
691
 		cursor_mode = CurPressed;
692
@@ -636,17 +770,34 @@ buttonpress(struct wl_listener *listener, void *data)
693
 		if (locked)
694
 			break;
695
 
696
+		if (!c && !exclusive_focus &&
697
+			(node = wlr_scene_node_at(&layers[LyrBottom]->node, cursor->x, cursor->y, NULL, NULL)) &&
698
+			(buffer = wlr_scene_buffer_from_node(node)) && buffer == selmon->scene_buffer) {
699
+			cx = (cursor->x - selmon->m.x) * selmon->wlr_output->scale;
700
+			do
701
+				x += TEXTW(selmon, tags[i]);
702
+			while (cx >= x && ++i < LENGTH(tags));
703
+			if (i < LENGTH(tags)) {
704
+				click = ClkTagBar;
705
+				arg.ui = 1 << i;
706
+			} else if (cx < x + TEXTW(selmon, selmon->ltsymbol))
707
+				click = ClkLtSymbol;
708
+			else if (cx > selmon->b.width - (TEXTW(selmon, stext) - selmon->lrpad + 2)) {
709
+				click = ClkStatus;
710
+			} else
711
+				click = ClkTitle;
712
+		}
713
+
714
 		/* Change focus if the button was _pressed_ over a client */
715
 		xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL);
716
-		if (c && (!client_is_unmanaged(c) || client_wants_focus(c)))
717
+		if (click == ClkClient && (!client_is_unmanaged(c) || client_wants_focus(c)))
718
 			focusclient(c, 1);
719
 
720
 		keyboard = wlr_seat_get_keyboard(seat);
721
 		mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
722
 		for (b = buttons; b < END(buttons); b++) {
723
-			if (CLEANMASK(mods) == CLEANMASK(b->mod) &&
724
-					event->button == b->button && b->func) {
725
-				b->func(&b->arg);
726
+			if (CLEANMASK(mods) == CLEANMASK(b->mod) && event->button == b->button && click == b->click && b->func) {
727
+				b->func(click == ClkTagBar && b->arg.i == 0 ? &arg : &b->arg);
728
 				return;
729
 			}
730
 		}
731
@@ -721,6 +872,8 @@ cleanup(void)
732
 	/* Destroy after the wayland display (when the monitors are already destroyed)
733
 	   to avoid destroying them with an invalid scene output. */
734
 	wlr_scene_node_destroy(&scene->tree.node);
735
+
736
+	drwl_fini();
737
 }
738
 
739
 void
740
@@ -736,6 +889,12 @@ cleanupmon(struct wl_listener *listener, void *data)
741
 			wlr_layer_surface_v1_destroy(l->layer_surface);
742
 	}
743
 
744
+	for (i = 0; i < LENGTH(m->pool); i++)
745
+		wlr_buffer_drop(&m->pool[i]->base);
746
+
747
+	drwl_setimage(m->drw, NULL);
748
+	drwl_destroy(m->drw);
749
+
750
 	wl_list_remove(&m->destroy.link);
751
 	wl_list_remove(&m->frame.link);
752
 	wl_list_remove(&m->link);
753
@@ -748,6 +907,7 @@ cleanupmon(struct wl_listener *listener, void *data)
754
 
755
 	closemon(m);
756
 	wlr_scene_node_destroy(&m->fullscreen_bg->node);
757
+	wlr_scene_node_destroy(&m->scene_buffer->node);
758
 	free(m);
759
 }
760
 
761
@@ -814,7 +974,7 @@ closemon(Monitor *m)
762
 			setmon(c, selmon, c->tags);
763
 	}
764
 	focusclient(focustop(selmon), 1);
765
-	printstatus();
766
+	drawbars();
767
 }
768
 
769
 void
770
@@ -1066,7 +1226,7 @@ createmon(struct wl_listener *listener, void *data)
771
 			m->nmaster = r->nmaster;
772
 			m->lt[0] = r->lt;
773
 			m->lt[1] = &layouts[LENGTH(layouts) > 1 && r->lt != &layouts[1]];
774
-			strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol));
775
+			strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof(m->ltsymbol));
776
 			wlr_output_state_set_scale(&state, r->scale);
777
 			wlr_output_state_set_transform(&state, r->rr);
778
 			break;
779
@@ -1088,8 +1248,15 @@ createmon(struct wl_listener *listener, void *data)
780
 	wlr_output_commit_state(wlr_output, &state);
781
 	wlr_output_state_finish(&state);
782
 
783
+	if (!(m->drw = drwl_create()))
784
+		die("failed to create drwl context");
785
+
786
+	m->scene_buffer = wlr_scene_buffer_create(layers[LyrBottom], NULL);
787
+	m->scene_buffer->point_accepts_input = baracceptsinput;
788
+	updatebar(m);
789
+
790
 	wl_list_insert(&mons, &m->link);
791
-	printstatus();
792
+	drawbars();
793
 
794
 	/* The xdg-protocol specifies:
795
 	 *
796
@@ -1399,6 +1566,80 @@ dirtomon(enum wlr_direction dir)
797
 	return selmon;
798
 }
799
 
800
+void
801
+drawbar(Monitor *m)
802
+{
803
+	int x, w, tw = 0;
804
+	int boxs = m->drw->font->height / 9;
805
+	int boxw = m->drw->font->height / 6 + 2;
806
+	uint32_t i, occ = 0, urg = 0;
807
+	Client *c;
808
+	Buffer *buf;
809
+
810
+	if (!m->scene_buffer->node.enabled)
811
+		return;
812
+	if (!(buf = bufmon(m)))
813
+		return;
814
+
815
+	/* draw status first so it can be overdrawn by tags later */
816
+	if (m == selmon) { /* status is only drawn on selected monitor */
817
+		drwl_setscheme(m->drw, colors[SchemeNorm]);
818
+		tw = TEXTW(m, stext) - m->lrpad + 2; /* 2px right padding */
819
+		drwl_text(m->drw, m->b.width - tw, 0, tw, m->b.height, 0, stext, 0);
820
+	}
821
+
822
+	wl_list_for_each(c, &clients, link) {
823
+		if (c->mon != m)
824
+			continue;
825
+		occ |= c->tags;
826
+		if (c->isurgent)
827
+			urg |= c->tags;
828
+	}
829
+	x = 0;
830
+	c = focustop(m);
831
+	for (i = 0; i < LENGTH(tags); i++) {
832
+		w = TEXTW(m, tags[i]);
833
+		drwl_setscheme(m->drw, colors[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]);
834
+		drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, tags[i], urg & 1 << i);
835
+		if (occ & 1 << i)
836
+			drwl_rect(m->drw, x + boxs, boxs, boxw, boxw,
837
+				m == selmon && c && c->tags & 1 << i,
838
+				urg & 1 << i);
839
+		x += w;
840
+	}
841
+	w = TEXTW(m, m->ltsymbol);
842
+	drwl_setscheme(m->drw, colors[SchemeNorm]);
843
+	x = drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, m->ltsymbol, 0);
844
+
845
+	if ((w = m->b.width - tw - x) > m->b.height) {
846
+		if (c) {
847
+			drwl_setscheme(m->drw, colors[m == selmon ? SchemeSel : SchemeNorm]);
848
+			drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, client_get_title(c), 0);
849
+			if (c && c->isfloating)
850
+				drwl_rect(m->drw, x + boxs, boxs, boxw, boxw, 0, 0);
851
+		} else {
852
+			drwl_setscheme(m->drw, colors[SchemeNorm]);
853
+			drwl_rect(m->drw, x, 0, w, m->b.height, 1, 1);
854
+		}
855
+	}
856
+
857
+	wlr_scene_buffer_set_dest_size(m->scene_buffer,
858
+		m->b.real_width, m->b.real_height);
859
+	wlr_scene_node_set_position(&m->scene_buffer->node, m->m.x,
860
+		m->m.y + (topbar ? 0 : m->m.height - m->b.real_height));
861
+	wlr_scene_buffer_set_buffer(m->scene_buffer, &buf->base);
862
+	wlr_buffer_unlock(&buf->base);
863
+}
864
+
865
+void
866
+drawbars(void)
867
+{
868
+	Monitor *m = NULL;
869
+
870
+	wl_list_for_each(m, &mons, link)
871
+		drawbar(m);
872
+}
873
+
874
 void
875
 focusclient(Client *c, int lift)
876
 {
877
@@ -1433,13 +1674,13 @@ focusclient(Client *c, int lift)
878
 		/* Don't change border color if there is an exclusive focus or we are
879
 		 * handling a drag operation */
880
 		if (!exclusive_focus && !seat->drag)
881
-			client_set_border_color(c, focuscolor);
882
+			client_set_border_color(c, (float[])COLOR(colors[SchemeSel][ColBorder]));
883
 	}
884
 
885
 	/* Deactivate old client if focus is changing */
886
 	if (old && (!c || client_surface(c) != old)) {
887
 		/* If an overlay is focused, don't focus or activate the client,
888
-		 * but only update its position in fstack to render its border with focuscolor
889
+		 * but only update its position in fstack to render its border with its color
890
 		 * and focus it after the overlay is closed. */
891
 		if (old_client_type == LayerShell && wlr_scene_node_coords(
892
 					&old_l->scene->node, &unused_lx, &unused_ly)
893
@@ -1450,12 +1691,11 @@ focusclient(Client *c, int lift)
894
 		/* Don't deactivate old client if the new one wants focus, as this causes issues with winecfg
895
 		 * and probably other clients */
896
 		} else if (old_c && !client_is_unmanaged(old_c) && (!c || !client_wants_focus(c))) {
897
-			client_set_border_color(old_c, bordercolor);
898
-
899
+			client_set_border_color(old_c, (float[])COLOR(colors[SchemeNorm][ColBorder]));
900
 			client_activate_surface(old, 0);
901
 		}
902
 	}
903
-	printstatus();
904
+	drawbars();
905
 
906
 	if (!c) {
907
 		/* With no client, all we have left is to clear focus */
908
@@ -1768,7 +2008,7 @@ mapnotify(struct wl_listener *listener, void *data)
909
 
910
 	for (i = 0; i < 4; i++) {
911
 		c->border[i] = wlr_scene_rect_create(c->scene, 0, 0,
912
-				c->isurgent ? urgentcolor : bordercolor);
913
+			(float[])COLOR(colors[c->isurgent ? SchemeUrg : SchemeNorm][ColBorder]));
914
 		c->border[i]->node.data = c;
915
 	}
916
 
917
@@ -1791,7 +2031,7 @@ mapnotify(struct wl_listener *listener, void *data)
918
 	} else {
919
 		applyrules(c);
920
 	}
921
-	printstatus();
922
+	drawbars();
923
 
924
 unset_fullscreen:
925
 	m = c->mon ? c->mon : xytomon(c->geom.x, c->geom.y);
926
@@ -2084,44 +2324,6 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy,
927
 	wlr_seat_pointer_notify_motion(seat, time, sx, sy);
928
 }
929
 
930
-void
931
-printstatus(void)
932
-{
933
-	Monitor *m = NULL;
934
-	Client *c;
935
-	uint32_t occ, urg, sel;
936
-
937
-	wl_list_for_each(m, &mons, link) {
938
-		occ = urg = 0;
939
-		wl_list_for_each(c, &clients, link) {
940
-			if (c->mon != m)
941
-				continue;
942
-			occ |= c->tags;
943
-			if (c->isurgent)
944
-				urg |= c->tags;
945
-		}
946
-		if ((c = focustop(m))) {
947
-			printf("%s title %s\n", m->wlr_output->name, client_get_title(c));
948
-			printf("%s appid %s\n", m->wlr_output->name, client_get_appid(c));
949
-			printf("%s fullscreen %d\n", m->wlr_output->name, c->isfullscreen);
950
-			printf("%s floating %d\n", m->wlr_output->name, c->isfloating);
951
-			sel = c->tags;
952
-		} else {
953
-			printf("%s title \n", m->wlr_output->name);
954
-			printf("%s appid \n", m->wlr_output->name);
955
-			printf("%s fullscreen \n", m->wlr_output->name);
956
-			printf("%s floating \n", m->wlr_output->name);
957
-			sel = 0;
958
-		}
959
-
960
-		printf("%s selmon %u\n", m->wlr_output->name, m == selmon);
961
-		printf("%s tags %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32"\n",
962
-			m->wlr_output->name, occ, m->tagset[m->seltags], sel, urg);
963
-		printf("%s layout %s\n", m->wlr_output->name, m->ltsymbol);
964
-	}
965
-	fflush(stdout);
966
-}
967
-
968
 void
969
 powermgrsetmode(struct wl_listener *listener, void *data)
970
 {
971
@@ -2250,22 +2452,14 @@ run(char *startup_cmd)
972
 
973
 	/* Now that the socket exists and the backend is started, run the startup command */
974
 	if (startup_cmd) {
975
-		int piperw[2];
976
-		if (pipe(piperw) < 0)
977
-			die("startup: pipe:");
978
 		if ((child_pid = fork()) < 0)
979
 			die("startup: fork:");
980
 		if (child_pid == 0) {
981
+			close(STDIN_FILENO);
982
 			setsid();
983
-			dup2(piperw[0], STDIN_FILENO);
984
-			close(piperw[0]);
985
-			close(piperw[1]);
986
 			execl("/bin/sh", "/bin/sh", "-c", startup_cmd, NULL);
987
 			die("startup: execl:");
988
 		}
989
-		dup2(piperw[1], STDOUT_FILENO);
990
-		close(piperw[1]);
991
-		close(piperw[0]);
992
 	}
993
 
994
 	/* Mark stdout as non-blocking to avoid the startup script
995
@@ -2275,7 +2469,7 @@ run(char *startup_cmd)
996
 	if (fd_set_nonblock(STDOUT_FILENO) < 0)
997
 		close(STDOUT_FILENO);
998
 
999
-	printstatus();
1000
+	drawbars();
1001
 
1002
 	/* At this point the outputs are initialized, choose initial selmon based on
1003
 	 * cursor position, and set default cursor image */
1004
@@ -2341,7 +2535,7 @@ setfloating(Client *c, int floating)
1005
 			(p && p->isfullscreen) ? LyrFS
1006
 			: c->isfloating ? LyrFloat : LyrTile]);
1007
 	arrange(c->mon);
1008
-	printstatus();
1009
+	drawbars();
1010
 }
1011
 
1012
 void
1013
@@ -2364,7 +2558,7 @@ setfullscreen(Client *c, int fullscreen)
1014
 		resize(c, c->prev, 0);
1015
 	}
1016
 	arrange(c->mon);
1017
-	printstatus();
1018
+	drawbars();
1019
 }
1020
 
1021
 void
1022
@@ -2376,9 +2570,9 @@ setlayout(const Arg *arg)
1023
 		selmon->sellt ^= 1;
1024
 	if (arg && arg->v)
1025
 		selmon->lt[selmon->sellt] = (Layout *)arg->v;
1026
-	strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, LENGTH(selmon->ltsymbol));
1027
+	strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof(selmon->ltsymbol));
1028
 	arrange(selmon);
1029
-	printstatus();
1030
+	drawbar(selmon);
1031
 }
1032
 
1033
 /* arg > 1.0 will set mfact absolutely */
1034
@@ -2451,6 +2645,7 @@ setup(void)
1035
 	for (i = 0; i < (int)LENGTH(sig); i++)
1036
 		sigaction(sig[i], &sa, NULL);
1037
 
1038
+
1039
 	wlr_log_init(log_level, NULL);
1040
 
1041
 	/* The Wayland display is managed by libwayland. It handles accepting
1042
@@ -2645,6 +2840,11 @@ setup(void)
1043
 	wl_signal_add(&output_mgr->events.apply, &output_mgr_apply);
1044
 	wl_signal_add(&output_mgr->events.test, &output_mgr_test);
1045
 
1046
+	drwl_init();
1047
+
1048
+	status_event_source = wl_event_loop_add_fd(wl_display_get_event_loop(dpy),
1049
+		STDIN_FILENO, WL_EVENT_READABLE, statusin, NULL);
1050
+
1051
 	/* Make sure XWayland clients don't connect to the parent X server,
1052
 	 * e.g when running in the x11 backend or the wayland backend and the
1053
 	 * compositor has Xwayland support */
1054
@@ -2669,6 +2869,7 @@ void
1055
 spawn(const Arg *arg)
1056
 {
1057
 	if (fork() == 0) {
1058
+		close(STDIN_FILENO);
1059
 		dup2(STDERR_FILENO, STDOUT_FILENO);
1060
 		setsid();
1061
 		execvp(((char **)arg->v)[0], (char **)arg->v);
1062
@@ -2687,6 +2888,30 @@ startdrag(struct wl_listener *listener, void *data)
1063
 	LISTEN_STATIC(&drag->icon->events.destroy, destroydragicon);
1064
 }
1065
 
1066
+int
1067
+statusin(int fd, unsigned int mask, void *data)
1068
+{
1069
+	char status[256];
1070
+	ssize_t n;
1071
+
1072
+	if (mask & WL_EVENT_ERROR)
1073
+		die("status in event error");
1074
+	if (mask & WL_EVENT_HANGUP)
1075
+		wl_event_source_remove(status_event_source);
1076
+
1077
+	n = read(fd, status, sizeof(status) - 1);
1078
+	if (n < 0 && errno != EWOULDBLOCK)
1079
+		die("read:");
1080
+
1081
+	status[n] = '\0';
1082
+	status[strcspn(status, "\n")] = '\0';
1083
+
1084
+	strncpy(stext, status, sizeof(stext));
1085
+	drawbars();
1086
+
1087
+	return 0;
1088
+}
1089
+
1090
 void
1091
 tag(const Arg *arg)
1092
 {
1093
@@ -2697,7 +2922,7 @@ tag(const Arg *arg)
1094
 	sel->tags = arg->ui & TAGMASK;
1095
 	focusclient(focustop(selmon), 1);
1096
 	arrange(selmon);
1097
-	printstatus();
1098
+	drawbars();
1099
 }
1100
 
1101
 void
1102
@@ -2742,6 +2967,14 @@ tile(Monitor *m)
1103
 	}
1104
 }
1105
 
1106
+void
1107
+togglebar(const Arg *arg)
1108
+{
1109
+	wlr_scene_node_set_enabled(&selmon->scene_buffer->node,
1110
+		!selmon->scene_buffer->node.enabled);
1111
+	arrangelayers(selmon);
1112
+}
1113
+
1114
 void
1115
 togglefloating(const Arg *arg)
1116
 {
1117
@@ -2770,7 +3003,7 @@ toggletag(const Arg *arg)
1118
 	sel->tags = newtags;
1119
 	focusclient(focustop(selmon), 1);
1120
 	arrange(selmon);
1121
-	printstatus();
1122
+	drawbars();
1123
 }
1124
 
1125
 void
1126
@@ -2783,7 +3016,7 @@ toggleview(const Arg *arg)
1127
 	selmon->tagset[selmon->seltags] = newtagset;
1128
 	focusclient(focustop(selmon), 1);
1129
 	arrange(selmon);
1130
-	printstatus();
1131
+	drawbars();
1132
 }
1133
 
1134
 void
1135
@@ -2831,7 +3064,7 @@ unmapnotify(struct wl_listener *listener, void *data)
1136
 	}
1137
 
1138
 	wlr_scene_node_destroy(&c->scene->node);
1139
-	printstatus();
1140
+	drawbars();
1141
 	motionnotify(0, NULL, 0, 0, 0, 0);
1142
 }
1143
 
1144
@@ -2931,6 +3164,13 @@ updatemons(struct wl_listener *listener, void *data)
1145
 		}
1146
 	}
1147
 
1148
+	if (stext[0] == '\0')
1149
+		strncpy(stext, "dwl-"VERSION, sizeof(stext));
1150
+	wl_list_for_each(m, &mons, link) {
1151
+		updatebar(m);
1152
+		drawbar(m);
1153
+	}
1154
+
1155
 	/* FIXME: figure out why the cursor image is at 0,0 after turning all
1156
 	 * the monitors on.
1157
 	 * Move the cursor image where it used to be. It does not generate a
1158
@@ -2941,12 +3181,45 @@ updatemons(struct wl_listener *listener, void *data)
1159
 	wlr_output_manager_v1_set_configuration(output_mgr, config);
1160
 }
1161
 
1162
+void
1163
+updatebar(Monitor *m)
1164
+{
1165
+	size_t i;
1166
+	int rw, rh;
1167
+	char fontattrs[12];
1168
+
1169
+	wlr_output_transformed_resolution(m->wlr_output, &rw, &rh);
1170
+	m->b.width = rw;
1171
+	m->b.real_width = (int)((float)m->b.width / m->wlr_output->scale);
1172
+
1173
+	wlr_scene_node_set_enabled(&m->scene_buffer->node, m->wlr_output->enabled ? showbar : 0);
1174
+
1175
+	for (i = 0; i < LENGTH(m->pool); i++)
1176
+		if (m->pool[i]) {
1177
+			wlr_buffer_drop(&m->pool[i]->base);
1178
+			m->pool[i] = NULL;
1179
+		}
1180
+
1181
+	if (m->b.scale == m->wlr_output->scale && m->drw)
1182
+		return;
1183
+
1184
+	drwl_font_destroy(m->drw->font);
1185
+	snprintf(fontattrs, sizeof(fontattrs), "dpi=%.2f", 96. * m->wlr_output->scale);
1186
+	if (!(drwl_font_create(m->drw, LENGTH(fonts), fonts, fontattrs)))
1187
+		die("Could not load font");
1188
+
1189
+	m->b.scale = m->wlr_output->scale;
1190
+	m->lrpad = m->drw->font->height;
1191
+	m->b.height = m->drw->font->height + 2;
1192
+	m->b.real_height = (int)((float)m->b.height / m->wlr_output->scale);
1193
+}
1194
+
1195
 void
1196
 updatetitle(struct wl_listener *listener, void *data)
1197
 {
1198
 	Client *c = wl_container_of(listener, c, set_title);
1199
 	if (c == focustop(c->mon))
1200
-		printstatus();
1201
+		drawbars();
1202
 }
1203
 
1204
 void
1205
@@ -2959,10 +3232,10 @@ urgent(struct wl_listener *listener, void *data)
1206
 		return;
1207
 
1208
 	c->isurgent = 1;
1209
-	printstatus();
1210
+	drawbars();
1211
 
1212
 	if (client_surface(c)->mapped)
1213
-		client_set_border_color(c, urgentcolor);
1214
+		client_set_border_color(c, (float[])COLOR(colors[SchemeUrg][ColBorder]));
1215
 }
1216
 
1217
 void
1218
@@ -2975,7 +3248,7 @@ view(const Arg *arg)
1219
 		selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
1220
 	focusclient(focustop(selmon), 1);
1221
 	arrange(selmon);
1222
-	printstatus();
1223
+	drawbars();
1224
 }
1225
 
1226
 void
1227
@@ -3016,6 +3289,7 @@ xytonode(double x, double y, struct wlr_surface **psurface,
1228
 {
1229
 	struct wlr_scene_node *node, *pnode;
1230
 	struct wlr_surface *surface = NULL;
1231
+	struct wlr_scene_surface *scene_surface = NULL;
1232
 	Client *c = NULL;
1233
 	LayerSurface *l = NULL;
1234
 	int layer;
1235
@@ -3024,9 +3298,12 @@ xytonode(double x, double y, struct wlr_surface **psurface,
1236
 		if (!(node = wlr_scene_node_at(&layers[layer]->node, x, y, nx, ny)))
1237
 			continue;
1238
 
1239
-		if (node->type == WLR_SCENE_NODE_BUFFER)
1240
-			surface = wlr_scene_surface_try_from_buffer(
1241
-					wlr_scene_buffer_from_node(node))->surface;
1242
+		if (node->type == WLR_SCENE_NODE_BUFFER) {
1243
+			scene_surface = wlr_scene_surface_try_from_buffer(
1244
+					wlr_scene_buffer_from_node(node));
1245
+			if (!scene_surface) continue;
1246
+			surface = scene_surface->surface;
1247
+		}
1248
 		/* Walk the tree to find a node that knows the client */
1249
 		for (pnode = node; pnode && !c; pnode = &pnode->parent->node)
1250
 			c = pnode->data;
1251
@@ -3159,10 +3436,10 @@ sethints(struct wl_listener *listener, void *data)
1252
 		return;
1253
 
1254
 	c->isurgent = xcb_icccm_wm_hints_get_urgency(c->surface.xwayland->hints);
1255
-	printstatus();
1256
+	drawbars();
1257
 
1258
 	if (c->isurgent && surface && surface->mapped)
1259
-		client_set_border_color(c, urgentcolor);
1260
+		client_set_border_color(c, (float[])COLOR(colors[SchemeUrg][ColBorder]));
1261
 }
1262
 
1263
 void
1264
-- 
1265
2.50.0