dotfiles

my shiny new dotfiles
git clone git://git.jakekoroman.com/dotfiles
Log | Files | Refs | README

dwlb.c (54149B)


      1 #define _GNU_SOURCE
      2 #include <ctype.h>
      3 #include <dirent.h>
      4 #include <errno.h>
      5 #include <fcft/fcft.h>
      6 #include <fcntl.h>
      7 #include <linux/input-event-codes.h>
      8 #include <pixman-1/pixman.h>
      9 #include <signal.h>
     10 #include <stdbool.h>
     11 #include <stdio.h>
     12 #include <stdlib.h>
     13 #include <string.h>
     14 #include <sys/mman.h>
     15 #include <sys/select.h>
     16 #include <sys/socket.h>
     17 #include <sys/stat.h>
     18 #include <sys/un.h>
     19 #include <unistd.h>
     20 #include <wayland-client.h>
     21 #include <wayland-cursor.h>
     22 #include <wayland-util.h>
     23 
     24 #include "utf8.h"
     25 #include "xdg-shell-protocol.h"
     26 #include "xdg-output-unstable-v1-protocol.h"
     27 #include "wlr-layer-shell-unstable-v1-protocol.h"
     28 #include "dwl-ipc-unstable-v2-protocol.h"
     29 
     30 #define DIE(fmt, ...)						\
     31 	do {							\
     32 		cleanup();					\
     33 		fprintf(stderr, fmt "\n", ##__VA_ARGS__);	\
     34 		exit(1);					\
     35 	} while (0)
     36 #define EDIE(fmt, ...)						\
     37 	DIE(fmt ": %s", ##__VA_ARGS__, strerror(errno));
     38 
     39 #define MIN(a, b)				\
     40 	((a) < (b) ? (a) : (b))
     41 #define MAX(a, b)				\
     42 	((a) > (b) ? (a) : (b))
     43 #define LENGTH(x)				\
     44 	(sizeof x / sizeof x[0])
     45 
     46 #define ARRAY_INIT_CAP 16
     47 #define ARRAY_EXPAND(arr, len, cap, inc)				\
     48 	do {								\
     49 		uint32_t new_len, new_cap;				\
     50 		new_len = (len) + (inc);				\
     51 		if (new_len > (cap)) {					\
     52 			new_cap = new_len * 2;				\
     53 			if (new_cap < ARRAY_INIT_CAP)			\
     54 				new_cap = ARRAY_INIT_CAP;		\
     55 			if (!((arr) = realloc((arr), sizeof(*(arr)) * new_cap))) \
     56 				EDIE("realloc");			\
     57 			(cap) = new_cap;				\
     58 		}							\
     59 		(len) = new_len;					\
     60 	} while (0)
     61 #define ARRAY_APPEND(arr, len, cap, ptr)		\
     62 	do {						\
     63 		ARRAY_EXPAND((arr), (len), (cap), 1);	\
     64 		(ptr) = &(arr)[(len) - 1];		\
     65 	} while (0)
     66 
     67 #define PROGRAM "dwlb"
     68 #define VERSION "0.2"
     69 #define USAGE								\
     70 	"usage: dwlb [OPTIONS]\n"					\
     71 	"Ipc\n"								\
     72 	"	-ipc				allow commands to be sent to dwl (dwl must be patched)\n" \
     73 	"	-no-ipc				disable ipc\n"		\
     74 	"Bar Config\n"							\
     75 	"	-hidden				bars will initially be hidden\n" \
     76 	"	-no-hidden			bars will not initially be hidden\n" \
     77 	"	-bottom				bars will initially be drawn at the bottom\n" \
     78 	"	-no-bottom			bars will initially be drawn at the top\n" \
     79 	"	-hide-vacant-tags		do not display empty and inactive tags\n" \
     80 	"	-no-hide-vacant-tags		display empty and inactive tags\n" \
     81 	"	-status-commands		enable in-line commands in status text\n" \
     82 	"	-no-status-commands		disable in-line commands in status text\n" \
     83 	"	-center-title			center title text on bar\n" \
     84 	"	-no-center-title		do not center title text on bar\n" \
     85 	"	-custom-title			do not display window title and treat the area as another status text element; see -title command\n" \
     86 	"	-no-custom-title		display current window title as normal\n" \
     87 	"	-font [FONT]			specify a font\n"	\
     88 	"	-tags [NUMBER] [FIRST]...[LAST]	if ipc is disabled, specify custom tag names. If NUMBER is 0, then no tag names should be given \n" \
     89 	"	-vertical-padding [PIXELS]	specify vertical pixel padding above and below text\n" \
     90 	"	-active-fg-color [COLOR]	specify text color of active tags or monitors\n" \
     91 	"	-active-bg-color [COLOR]	specify background color of active tags or monitors\n" \
     92 	"	-occupied-fg-color [COLOR]	specify text color of occupied tags\n" \
     93 	"	-occupied-bg-color [COLOR]	specify background color of occupied tags\n" \
     94 	"	-inactive-fg-color [COLOR]	specify text color of inactive tags or monitors\n" \
     95 	"	-inactive-bg-color [COLOR]	specify background color of inactive tags or monitors\n" \
     96 	"	-urgent-fg-color [COLOR]	specify text color of urgent tags\n" \
     97 	"	-urgent-bg-color [COLOR]	specify background color of urgent tags\n" \
     98 	"	-middle-bg-color [COLOR]	specify background color of the color in the middle of the bar\n" \
     99 	"	-middle-bg-color-selected [COLOR]	specify background color of the color in the middle of the bar, when selected\n" \
    100 	"	-scale [BUFFER_SCALE]		specify buffer scale value for integer scaling\n" \
    101 	"Commands\n"							\
    102 	"	-target-socket [SOCKET-NAME]	set the socket to send command to. Sockets can be found in `$XDG_RUNTIME_DIR/dwlb/`\n"\
    103 	"	-status	[OUTPUT] [TEXT]		set status text\n"	\
    104 	"	-status-stdin	[OUTPUT]		set status text from stdin\n"	\
    105 	"	-title	[OUTPUT] [TEXT]		set title text, if -custom-title is enabled\n"	\
    106 	"	-show [OUTPUT]			show bar\n"		\
    107 	"	-hide [OUTPUT]			hide bar\n"		\
    108 	"	-toggle-visibility [OUTPUT]	toggle bar visibility\n" \
    109 	"	-set-top [OUTPUT]		draw bar at the top\n"	\
    110 	"	-set-bottom [OUTPUT]		draw bar at the bottom\n" \
    111 	"	-toggle-location [OUTPUT]	toggle bar location\n"	\
    112 	"Other\n"							\
    113 	"	-v				get version information\n" \
    114 	"	-h				view this help text\n"
    115 
    116 #define TEXT_MAX 2048
    117 
    118 typedef struct {
    119 	pixman_color_t color;
    120 	bool bg;
    121 	char *start;
    122 } Color;
    123 
    124 typedef struct {
    125 	uint32_t btn;
    126 	uint32_t x1;
    127 	uint32_t x2;
    128 	char command[128];
    129 } Button;
    130 
    131 typedef struct {
    132 	char text[TEXT_MAX];
    133 	Color *colors;
    134 	uint32_t colors_l, colors_c;
    135 	Button *buttons;
    136 	uint32_t buttons_l, buttons_c;
    137 } CustomText;
    138 
    139 typedef struct {
    140 	struct wl_output *wl_output;
    141 	struct wl_surface *wl_surface;
    142 	struct zwlr_layer_surface_v1 *layer_surface;
    143 	struct zxdg_output_v1 *xdg_output;
    144 	struct zdwl_ipc_output_v2 *dwl_wm_output;
    145 
    146 	uint32_t registry_name;
    147 	char *xdg_output_name;
    148 
    149 	bool configured;
    150 	uint32_t width, height;
    151 	uint32_t textpadding;
    152 	uint32_t stride, bufsize;
    153 	
    154 	uint32_t mtags, ctags, urg, sel;
    155 	char *layout, *window_title;
    156 	uint32_t layout_idx, last_layout_idx;
    157 	CustomText title, status;
    158 
    159 	bool hidden, bottom;
    160 	bool redraw;
    161 
    162 	struct wl_list link;
    163 } Bar;
    164 
    165 typedef struct {
    166 	struct wl_seat *wl_seat;
    167 	struct wl_pointer *wl_pointer;
    168 	uint32_t registry_name;
    169 
    170 	Bar *bar;
    171 	uint32_t pointer_x, pointer_y;
    172 	uint32_t pointer_button;
    173 
    174 	struct wl_list link;
    175 } Seat;
    176 
    177 static int sock_fd;
    178 static char socketdir[256];
    179 static char *socketpath;
    180 static char sockbuf[4096];
    181 
    182 static char *stdinbuf;
    183 static size_t stdinbuf_cap;
    184 
    185 static struct wl_display *display;
    186 static struct wl_compositor *compositor;
    187 static struct wl_shm *shm;
    188 static struct zwlr_layer_shell_v1 *layer_shell;
    189 static struct zxdg_output_manager_v1 *output_manager;
    190 
    191 static struct zdwl_ipc_manager_v2 *dwl_wm;
    192 static struct wl_cursor_image *cursor_image;
    193 static struct wl_surface *cursor_surface;
    194 
    195 static struct wl_list bar_list, seat_list;
    196 
    197 static char **tags;
    198 static uint32_t tags_l, tags_c;
    199 static char **layouts;
    200 static uint32_t layouts_l, layouts_c;
    201 
    202 static struct fcft_font *font;
    203 static uint32_t height, textpadding, buffer_scale;
    204 
    205 static bool run_display;
    206 
    207 #include "config.h"
    208 
    209 static void
    210 wl_buffer_release(void *data, struct wl_buffer *wl_buffer)
    211 {
    212 	/* Sent by the compositor when it's no longer using this buffer */
    213 	wl_buffer_destroy(wl_buffer);
    214 }
    215 
    216 static const struct wl_buffer_listener wl_buffer_listener = {
    217 	.release = wl_buffer_release,
    218 };
    219 
    220 /* Shared memory support function adapted from [wayland-book] */
    221 static int
    222 allocate_shm_file(size_t size)
    223 {
    224 	int fd = memfd_create("surface", MFD_CLOEXEC);
    225 	if (fd == -1)
    226 		return -1;
    227 	int ret;
    228 	do {
    229 		ret = ftruncate(fd, size);
    230 	} while (ret == -1 && errno == EINTR);
    231 	if (ret == -1) {
    232 		close(fd);
    233 		return -1;
    234 	}
    235 	return fd;
    236 }
    237 
    238 static uint32_t
    239 draw_text(char *text,
    240 	  uint32_t x,
    241 	  uint32_t y,
    242 	  pixman_image_t *foreground,
    243 	  pixman_image_t *background,
    244 	  pixman_color_t *fg_color,
    245 	  pixman_color_t *bg_color,
    246 	  uint32_t max_x,
    247 	  uint32_t buf_height,
    248 	  uint32_t padding,
    249 	  Color *colors,
    250 	  uint32_t colors_l)
    251 {
    252 	if (!text || !*text || !max_x)
    253 		return x;
    254 
    255 	uint32_t ix = x, nx;
    256 
    257 	if ((nx = x + padding) + padding >= max_x)
    258 		return x;
    259 	x = nx;
    260 
    261 	bool draw_fg = foreground && fg_color;
    262 	bool draw_bg = background && bg_color;
    263 	
    264 	pixman_image_t *fg_fill;
    265 	pixman_color_t *cur_bg_color;
    266 	if (draw_fg)
    267 		fg_fill = pixman_image_create_solid_fill(fg_color);
    268 	if (draw_bg)
    269 		cur_bg_color = bg_color;
    270 
    271 	uint32_t color_ind = 0, codepoint, state = UTF8_ACCEPT, last_cp = 0;
    272 	for (char *p = text; *p; p++) {
    273 		/* Check for new colors */
    274 		if (state == UTF8_ACCEPT && colors && (draw_fg || draw_bg)) {
    275 			while (color_ind < colors_l && p == colors[color_ind].start) {
    276 				if (colors[color_ind].bg) {
    277 					if (draw_bg)
    278 						cur_bg_color = &colors[color_ind].color;
    279 				} else if (draw_fg) {
    280 					pixman_image_unref(fg_fill);
    281 					fg_fill = pixman_image_create_solid_fill(&colors[color_ind].color);
    282 				}
    283 				color_ind++;
    284 			}
    285 		}
    286 		
    287 		/* Returns nonzero if more bytes are needed */
    288 		if (utf8decode(&state, &codepoint, *p))
    289 			continue;
    290 
    291 		/* Turn off subpixel rendering, which complicates things when
    292 		 * mixed with alpha channels */
    293 		const struct fcft_glyph *glyph = fcft_rasterize_char_utf32(font, codepoint, FCFT_SUBPIXEL_NONE);
    294 		if (!glyph)
    295 			continue;
    296 
    297 		/* Adjust x position based on kerning with previous glyph */
    298 		long kern = 0;
    299 		if (last_cp)
    300 			fcft_kerning(font, last_cp, codepoint, &kern, NULL);
    301 		if ((nx = x + kern + glyph->advance.x) + padding > max_x)
    302 			break;
    303 		last_cp = codepoint;
    304 		x += kern;
    305 
    306 		if (draw_fg) {
    307 			/* Detect and handle pre-rendered glyphs (e.g. emoji) */
    308 			if (pixman_image_get_format(glyph->pix) == PIXMAN_a8r8g8b8) {
    309 				/* Only the alpha channel of the mask is used, so we can
    310 				 * use fgfill here to blend prerendered glyphs with the
    311 				 * same opacity */
    312 				pixman_image_composite32(
    313 					PIXMAN_OP_OVER, glyph->pix, fg_fill, foreground, 0, 0, 0, 0,
    314 					x + glyph->x, y - glyph->y, glyph->width, glyph->height);
    315 			} else {
    316 				/* Applying the foreground color here would mess up
    317 				 * component alphas for subpixel-rendered text, so we
    318 				 * apply it when blending. */
    319 				pixman_image_composite32(
    320 					PIXMAN_OP_OVER, fg_fill, glyph->pix, foreground, 0, 0, 0, 0,
    321 					x + glyph->x, y - glyph->y, glyph->width, glyph->height);
    322 			}
    323 		}
    324 		
    325 		if (draw_bg) {
    326 			pixman_image_fill_boxes(PIXMAN_OP_OVER, background,
    327 						cur_bg_color, 1, &(pixman_box32_t){
    328 							.x1 = x, .x2 = nx,
    329 							.y1 = 0, .y2 = buf_height
    330 						});
    331 		}
    332 		
    333 		/* increment pen position */
    334 		x = nx;
    335 	}
    336 	
    337 	if (draw_fg)
    338 		pixman_image_unref(fg_fill);
    339 	if (!last_cp)
    340 		return ix;
    341 	
    342 	nx = x + padding;
    343 	if (draw_bg) {
    344 		/* Fill padding background */
    345 		pixman_image_fill_boxes(PIXMAN_OP_OVER, background,
    346 					bg_color, 1, &(pixman_box32_t){
    347 						.x1 = ix, .x2 = ix + padding,
    348 						.y1 = 0, .y2 = buf_height
    349 					});
    350 		pixman_image_fill_boxes(PIXMAN_OP_OVER, background,
    351 					bg_color, 1, &(pixman_box32_t){
    352 						.x1 = x, .x2 = nx,
    353 						.y1 = 0, .y2 = buf_height
    354 					});
    355 	}
    356 	
    357 	return nx;
    358 }
    359 
    360 #define TEXT_WIDTH(text, maxwidth, padding)				\
    361 	draw_text(text, 0, 0, NULL, NULL, NULL, NULL, maxwidth, 0, padding, NULL, 0)
    362 
    363 static int
    364 draw_frame(Bar *bar)
    365 {
    366 	/* Allocate buffer to be attached to the surface */
    367         int fd = allocate_shm_file(bar->bufsize);
    368 	if (fd == -1)
    369 		return -1;
    370 
    371 	uint32_t *data = mmap(NULL, bar->bufsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    372 	if (data == MAP_FAILED) {
    373 		close(fd);
    374 		return -1;
    375 	}
    376 
    377 	struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, bar->bufsize);
    378 	struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, bar->width, bar->height, bar->stride, WL_SHM_FORMAT_ARGB8888);
    379 	wl_buffer_add_listener(buffer, &wl_buffer_listener, NULL);
    380 	wl_shm_pool_destroy(pool);
    381 	close(fd);
    382 
    383 	/* Pixman image corresponding to main buffer */
    384 	pixman_image_t *final = pixman_image_create_bits(PIXMAN_a8r8g8b8, bar->width, bar->height, data, bar->width * 4);
    385 	
    386 	/* Text background and foreground layers */
    387 	pixman_image_t *foreground = pixman_image_create_bits(PIXMAN_a8r8g8b8, bar->width, bar->height, NULL, bar->width * 4);
    388 	pixman_image_t *background = pixman_image_create_bits(PIXMAN_a8r8g8b8, bar->width, bar->height, NULL, bar->width * 4);
    389 	
    390 	/* Draw on images */
    391 	uint32_t x = 0;
    392 	uint32_t y = (bar->height + font->ascent - font->descent) / 2;
    393 	uint32_t boxs = font->height / 9;
    394 	uint32_t boxw = font->height / 6 + 2;
    395 
    396 	for (uint32_t i = 0; i < tags_l; i++) {
    397 		const bool active = bar->mtags & 1 << i;
    398 		const bool occupied = bar->ctags & 1 << i;
    399 		const bool urgent = bar->urg & 1 << i;
    400 		
    401 		if (hide_vacant && !active && !occupied && !urgent)
    402 			continue;
    403 
    404 		pixman_color_t *fg_color = urgent ? &urgent_fg_color : (active ? &active_fg_color : (occupied ? &occupied_fg_color : &inactive_fg_color));
    405 		pixman_color_t *bg_color = urgent ? &urgent_bg_color : (active ? &active_bg_color : (occupied ? &occupied_bg_color : &inactive_bg_color));
    406 		
    407 		if (!hide_vacant && occupied) {
    408 			pixman_image_fill_boxes(PIXMAN_OP_SRC, foreground,
    409 						fg_color, 1, &(pixman_box32_t){
    410 							.x1 = x + boxs, .x2 = x + boxs + boxw,
    411 							.y1 = boxs, .y2 = boxs + boxw
    412 						});
    413 			if ((!bar->sel || !active) && boxw >= 3) {
    414 				/* Make box hollow */
    415 				pixman_image_fill_boxes(PIXMAN_OP_SRC, foreground,
    416 							&(pixman_color_t){ 0 },
    417 							1, &(pixman_box32_t){
    418 								.x1 = x + boxs + 1, .x2 = x + boxs + boxw - 1,
    419 								.y1 = boxs + 1, .y2 = boxs + boxw - 1
    420 							});
    421 			}
    422 		}
    423 		
    424 		x = draw_text(tags[i], x, y, foreground, background, fg_color, bg_color,
    425 			      bar->width, bar->height, bar->textpadding, NULL, 0);
    426 	}
    427 	
    428 	x = draw_text(bar->layout, x, y, foreground, background,
    429 		      &inactive_fg_color, &inactive_bg_color, bar->width,
    430 		      bar->height, bar->textpadding, NULL, 0);
    431 	
    432 	uint32_t status_width = TEXT_WIDTH(bar->status.text, bar->width - x, bar->textpadding);
    433 	draw_text(bar->status.text, bar->width - status_width, y, foreground,
    434 		  background, &inactive_fg_color, &inactive_bg_color,
    435 		  bar->width, bar->height, bar->textpadding,
    436 		  bar->status.colors, bar->status.colors_l);
    437 
    438 	uint32_t nx;
    439 	if (center_title) {
    440 		uint32_t title_width = TEXT_WIDTH(custom_title ? bar->title.text : bar->window_title, bar->width - status_width - x, 0);
    441 		nx = MAX(x, MIN((bar->width - title_width) / 2, bar->width - status_width - title_width));
    442 	} else {
    443 		nx = MIN(x + bar->textpadding, bar->width - status_width);
    444 	}
    445 	pixman_image_fill_boxes(PIXMAN_OP_SRC, background,
    446 				bar->sel ? &middle_bg_color_selected : &middle_bg_color, 1,
    447 				&(pixman_box32_t){
    448 					.x1 = x, .x2 = nx,
    449 					.y1 = 0, .y2 = bar->height
    450 				});
    451 	x = nx;
    452 	
    453 	x = draw_text(custom_title ? bar->title.text : bar->window_title,
    454 		      x, y, foreground, background,
    455 		      bar->sel ? &active_fg_color : &inactive_fg_color,
    456 		      bar->sel ? &active_bg_color : &inactive_bg_color,
    457 		      bar->width - status_width, bar->height, 0,
    458 		      custom_title ? bar->title.colors : NULL,
    459 		      custom_title ? bar->title.colors_l : 0);
    460 
    461 	pixman_image_fill_boxes(PIXMAN_OP_SRC, background,
    462 				bar->sel ? &middle_bg_color_selected : &middle_bg_color, 1,
    463 				&(pixman_box32_t){
    464 					.x1 = x, .x2 = bar->width - status_width,
    465 					.y1 = 0, .y2 = bar->height
    466 				});
    467 
    468 	/* Draw background and foreground on bar */
    469 	pixman_image_composite32(PIXMAN_OP_OVER, background, NULL, final, 0, 0, 0, 0, 0, 0, bar->width, bar->height);
    470 	pixman_image_composite32(PIXMAN_OP_OVER, foreground, NULL, final, 0, 0, 0, 0, 0, 0, bar->width, bar->height);
    471 
    472 	pixman_image_unref(foreground);
    473 	pixman_image_unref(background);
    474 	pixman_image_unref(final);
    475 	
    476 	munmap(data, bar->bufsize);
    477 
    478 	wl_surface_set_buffer_scale(bar->wl_surface, buffer_scale);
    479 	wl_surface_attach(bar->wl_surface, buffer, 0, 0);
    480 	wl_surface_damage_buffer(bar->wl_surface, 0, 0, bar->width, bar->height);
    481 	wl_surface_commit(bar->wl_surface);
    482 
    483 	return 0;
    484 }
    485 
    486 /* Layer-surface setup adapted from layer-shell example in [wlroots] */
    487 static void
    488 layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface,
    489 			uint32_t serial, uint32_t w, uint32_t h)
    490 {
    491 	w = w * buffer_scale;
    492 	h = h * buffer_scale;
    493 
    494 	zwlr_layer_surface_v1_ack_configure(surface, serial);
    495 
    496 	Bar *bar = (Bar *)data;
    497 
    498 	if (bar->configured && w == bar->width && h == bar->height)
    499 		return;
    500 
    501 	bar->width = w;
    502 	bar->height = h;
    503 	bar->stride = bar->width * 4;
    504 	bar->bufsize = bar->stride * bar->height;
    505 	bar->configured = true;
    506 
    507 	draw_frame(bar);
    508 }
    509 
    510 static void
    511 layer_surface_closed(void *data, struct zwlr_layer_surface_v1 *surface)
    512 {
    513     /*
    514      * https://github.com/kolunmi/dwlb/issues/39
    515      *
    516      * NOTE(jake): this means the bar will never gracefully close but who cares.
    517      * if i close dwl then most likely im turning off my computer so it doesnt matter
    518      * how graceful it wants to be.
    519      */
    520 
    521     //run_display = false;
    522 }
    523 
    524 static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
    525 	.configure = layer_surface_configure,
    526 	.closed = layer_surface_closed,
    527 };
    528 
    529 static void
    530 cleanup(void)
    531 {
    532 	if (socketpath)
    533 		unlink(socketpath);
    534 }
    535 
    536 static void
    537 output_name(void *data, struct zxdg_output_v1 *xdg_output, const char *name)
    538 {
    539 	Bar *bar = (Bar *)data;
    540 	
    541 	if (bar->xdg_output_name)
    542 		free(bar->xdg_output_name);
    543 	if (!(bar->xdg_output_name = strdup(name)))
    544 		EDIE("strdup");
    545 }
    546 
    547 static void
    548 output_logical_position(void *data, struct zxdg_output_v1 *xdg_output,
    549 			int32_t x, int32_t y)
    550 {
    551 }
    552 
    553 static void
    554 output_logical_size(void *data, struct zxdg_output_v1 *xdg_output,
    555 		    int32_t width, int32_t height)
    556 {
    557 }
    558 
    559 static void
    560 output_done(void *data, struct zxdg_output_v1 *xdg_output)
    561 {
    562 }
    563 
    564 static void
    565 output_description(void *data, struct zxdg_output_v1 *xdg_output,
    566 		   const char *description)
    567 {
    568 }
    569 
    570 static const struct zxdg_output_v1_listener output_listener = {
    571 	.name = output_name,
    572 	.logical_position = output_logical_position,
    573 	.logical_size = output_logical_size,
    574 	.done = output_done,
    575 	.description = output_description
    576 };
    577 
    578 static void
    579 shell_command(char *command)
    580 {
    581 	if (fork() == 0) {
    582 		setsid();
    583 		execl("/bin/sh", "sh", "-c", command, NULL);
    584 		exit(EXIT_SUCCESS);
    585 	}
    586 }
    587 
    588 static void
    589 pointer_enter(void *data, struct wl_pointer *pointer,
    590 	      uint32_t serial, struct wl_surface *surface,
    591 	      wl_fixed_t surface_x, wl_fixed_t surface_y)
    592 {
    593 	Seat *seat = (Seat *)data;
    594 
    595 	seat->bar = NULL;
    596 	Bar *bar;
    597 	wl_list_for_each(bar, &bar_list, link) {
    598 		if (bar->wl_surface == surface) {
    599 			seat->bar = bar;
    600 			break;
    601 		}
    602 	}
    603 
    604 	if (!cursor_image) {
    605 		const char *size_str = getenv("XCURSOR_SIZE");
    606 		int size = size_str ? atoi(size_str) : 0;
    607 		if (size == 0)
    608 			size = 24;
    609 		struct wl_cursor_theme *cursor_theme = wl_cursor_theme_load(getenv("XCURSOR_THEME"), size * buffer_scale, shm);
    610 		cursor_image = wl_cursor_theme_get_cursor(cursor_theme, "left_ptr")->images[0];
    611 		cursor_surface = wl_compositor_create_surface(compositor);
    612 		wl_surface_set_buffer_scale(cursor_surface, buffer_scale);
    613 		wl_surface_attach(cursor_surface, wl_cursor_image_get_buffer(cursor_image), 0, 0);
    614 		wl_surface_commit(cursor_surface);
    615 	}
    616 	wl_pointer_set_cursor(pointer, serial, cursor_surface,
    617 			      cursor_image->hotspot_x,
    618 			      cursor_image->hotspot_y);
    619 }
    620 
    621 static void
    622 pointer_leave(void *data, struct wl_pointer *pointer,
    623 	      uint32_t serial, struct wl_surface *surface)
    624 {
    625 	Seat *seat = (Seat *)data;
    626 	
    627 	seat->bar = NULL;
    628 }
    629 
    630 static void
    631 pointer_button(void *data, struct wl_pointer *pointer, uint32_t serial,
    632 	       uint32_t time, uint32_t button, uint32_t state)
    633 {
    634 	Seat *seat = (Seat *)data;
    635 
    636 	seat->pointer_button = state == WL_POINTER_BUTTON_STATE_PRESSED ? button : 0;
    637 }
    638 
    639 static void
    640 pointer_motion(void *data, struct wl_pointer *pointer, uint32_t time,
    641 	       wl_fixed_t surface_x, wl_fixed_t surface_y)
    642 {
    643 	Seat *seat = (Seat *)data;
    644 	
    645 	seat->pointer_x = wl_fixed_to_int(surface_x);
    646 	seat->pointer_y = wl_fixed_to_int(surface_y);
    647 }
    648 
    649 static void
    650 pointer_frame(void *data, struct wl_pointer *pointer)
    651 {
    652 	Seat *seat = (Seat *)data;
    653 
    654 	if (!seat->pointer_button || !seat->bar)
    655 		return;
    656 
    657 	uint32_t x = 0, i = 0;
    658 	do {
    659 		if (hide_vacant) {
    660 			const bool active = seat->bar->mtags & 1 << i;
    661 			const bool occupied = seat->bar->ctags & 1 << i;
    662 			const bool urgent = seat->bar->urg & 1 << i;
    663 			if (!active && !occupied && !urgent)
    664 				continue;
    665 		}
    666 		x += TEXT_WIDTH(tags[i], seat->bar->width - x, seat->bar->textpadding) / buffer_scale;
    667 	} while (seat->pointer_x >= x && ++i < tags_l);
    668 	
    669 	if (i < tags_l) {
    670 		/* Clicked on tags */
    671 		if (ipc) {
    672 			if (seat->pointer_button == BTN_LEFT)
    673 				zdwl_ipc_output_v2_set_tags(seat->bar->dwl_wm_output, 1 << i, 1);
    674 			else if (seat->pointer_button == BTN_MIDDLE)
    675 				zdwl_ipc_output_v2_set_tags(seat->bar->dwl_wm_output, ~0, 1);
    676 			else if (seat->pointer_button == BTN_RIGHT)
    677 				zdwl_ipc_output_v2_set_tags(seat->bar->dwl_wm_output, seat->bar->mtags ^ (1 << i), 0);
    678 		}
    679 	} else if (seat->pointer_x < (x += TEXT_WIDTH(seat->bar->layout, seat->bar->width - x, seat->bar->textpadding))) {
    680 		/* Clicked on layout */
    681 		if (ipc) {
    682 			if (seat->pointer_button == BTN_LEFT)
    683 				zdwl_ipc_output_v2_set_layout(seat->bar->dwl_wm_output, seat->bar->last_layout_idx);
    684 			else if (seat->pointer_button == BTN_RIGHT)
    685 				zdwl_ipc_output_v2_set_layout(seat->bar->dwl_wm_output, 2);
    686 		}
    687 	} else {
    688 		uint32_t status_x = seat->bar->width / buffer_scale - TEXT_WIDTH(seat->bar->status.text, seat->bar->width - x, seat->bar->textpadding) / buffer_scale;
    689 		if (seat->pointer_x < status_x) {
    690 			/* Clicked on title */
    691 			if (custom_title) {
    692 				if (center_title) {
    693 					uint32_t title_width = TEXT_WIDTH(seat->bar->title.text, status_x - x, 0);
    694 					x = MAX(x, MIN((seat->bar->width - title_width) / 2, status_x - title_width));
    695 				} else {
    696 					x = MIN(x + seat->bar->textpadding, status_x);
    697 				}
    698 				for (i = 0; i < seat->bar->title.buttons_l; i++) {
    699 					if (seat->pointer_button == seat->bar->title.buttons[i].btn
    700 					    && seat->pointer_x >= x + seat->bar->title.buttons[i].x1
    701 					    && seat->pointer_x < x + seat->bar->title.buttons[i].x2) {
    702 						shell_command(seat->bar->title.buttons[i].command);
    703 						break;
    704 					}
    705 				}
    706 			}
    707 		} else {
    708 			/* Clicked on status */
    709 			for (i = 0; i < seat->bar->status.buttons_l; i++) {
    710 				if (seat->pointer_button == seat->bar->status.buttons[i].btn
    711 				    && seat->pointer_x >= status_x + seat->bar->textpadding + seat->bar->status.buttons[i].x1 / buffer_scale
    712 				    && seat->pointer_x < status_x + seat->bar->textpadding + seat->bar->status.buttons[i].x2 / buffer_scale) {
    713 					shell_command(seat->bar->status.buttons[i].command);
    714 					break;
    715 				}
    716 			}
    717 		}
    718 	}
    719 	
    720 	seat->pointer_button = 0;
    721 }
    722 
    723 static void
    724 pointer_axis(void *data, struct wl_pointer *pointer,
    725 	     uint32_t time, uint32_t axis, wl_fixed_t value)
    726 {
    727 }
    728 
    729 static void
    730 pointer_axis_discrete(void *data, struct wl_pointer *pointer,
    731 		      uint32_t axis, int32_t discrete)
    732 {
    733 }
    734 
    735 static void
    736 pointer_axis_source(void *data, struct wl_pointer *pointer,
    737 		    uint32_t axis_source)
    738 {
    739 }
    740 
    741 static void
    742 pointer_axis_stop(void *data, struct wl_pointer *pointer,
    743 		  uint32_t time, uint32_t axis)
    744 {
    745 }
    746 
    747 static void
    748 pointer_axis_value120(void *data, struct wl_pointer *pointer,
    749 		      uint32_t axis, int32_t discrete)
    750 {
    751 }
    752 
    753 static const struct wl_pointer_listener pointer_listener = {
    754 	.axis = pointer_axis,
    755 	.axis_discrete = pointer_axis_discrete,
    756 	.axis_source = pointer_axis_source,
    757 	.axis_stop = pointer_axis_stop,
    758 	.axis_value120 = pointer_axis_value120,
    759 	.button = pointer_button,
    760 	.enter = pointer_enter,
    761 	.frame = pointer_frame,
    762 	.leave = pointer_leave,
    763 	.motion = pointer_motion,
    764 };
    765 
    766 static void
    767 seat_capabilities(void *data, struct wl_seat *wl_seat,
    768 		  uint32_t capabilities)
    769 {
    770 	Seat *seat = (Seat *)data;
    771 	
    772 	uint32_t has_pointer = capabilities & WL_SEAT_CAPABILITY_POINTER;
    773 	if (has_pointer && !seat->wl_pointer) {
    774 		seat->wl_pointer = wl_seat_get_pointer(seat->wl_seat);
    775 		wl_pointer_add_listener(seat->wl_pointer, &pointer_listener, seat);
    776 	} else if (!has_pointer && seat->wl_pointer) {
    777 		wl_pointer_destroy(seat->wl_pointer);
    778 		seat->wl_pointer = NULL;
    779 	}
    780 }
    781 
    782 static void
    783 seat_name(void *data, struct wl_seat *wl_seat, const char *name)
    784 {
    785 }
    786 
    787 static const struct wl_seat_listener seat_listener = {
    788 	.capabilities = seat_capabilities,
    789 	.name = seat_name,
    790 };
    791 
    792 static void
    793 show_bar(Bar *bar)
    794 {
    795 	bar->wl_surface = wl_compositor_create_surface(compositor);
    796 	if (!bar->wl_surface)
    797 		DIE("Could not create wl_surface");
    798 
    799 	bar->layer_surface = zwlr_layer_shell_v1_get_layer_surface(layer_shell, bar->wl_surface, bar->wl_output,
    800 								   ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, PROGRAM);
    801 	if (!bar->layer_surface)
    802 		DIE("Could not create layer_surface");
    803 	zwlr_layer_surface_v1_add_listener(bar->layer_surface, &layer_surface_listener, bar);
    804 
    805 	zwlr_layer_surface_v1_set_size(bar->layer_surface, 0, bar->height / buffer_scale);
    806 	zwlr_layer_surface_v1_set_anchor(bar->layer_surface,
    807 					 (bar->bottom ? ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM : ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)
    808 					 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
    809 					 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT);
    810 	zwlr_layer_surface_v1_set_exclusive_zone(bar->layer_surface, bar->height / buffer_scale);
    811 	wl_surface_commit(bar->wl_surface);
    812 
    813 	bar->hidden = false;
    814 }
    815 
    816 static void
    817 hide_bar(Bar *bar)
    818 {
    819 	zwlr_layer_surface_v1_destroy(bar->layer_surface);
    820 	wl_surface_destroy(bar->wl_surface);
    821 
    822 	bar->configured = false;
    823 	bar->hidden = true;
    824 }
    825 
    826 static void
    827 dwl_wm_tags(void *data, struct zdwl_ipc_manager_v2 *dwl_wm,
    828 	uint32_t amount)
    829 {
    830 	if (!tags && !(tags = malloc(amount * sizeof(char *))))
    831 		EDIE("malloc");
    832 	uint32_t i = tags_l;
    833 	ARRAY_EXPAND(tags, tags_l, tags_c, MAX(0, (int)amount - (int)tags_l));
    834 	for (; i < amount; i++)
    835 		if (!(tags[i] = strdup(tags_names[MIN(i, LENGTH(tags_names)-1)])))
    836 			EDIE("strdup");
    837 }
    838 
    839 static void
    840 dwl_wm_layout(void *data, struct zdwl_ipc_manager_v2 *dwl_wm,
    841 	const char *name)
    842 {
    843 	char **ptr;
    844 	ARRAY_APPEND(layouts, layouts_l, layouts_c, ptr);
    845 	if (!(*ptr = strdup(name)))
    846 		EDIE("strdup");
    847 }
    848 
    849 static const struct zdwl_ipc_manager_v2_listener dwl_wm_listener = {
    850 	.tags = dwl_wm_tags,
    851 	.layout = dwl_wm_layout
    852 };
    853 
    854 static void
    855 dwl_wm_output_toggle_visibility(void *data, struct zdwl_ipc_output_v2 *dwl_wm_output)
    856 {
    857 	Bar *bar = (Bar *)data;
    858 
    859 	if (bar->hidden)
    860 		show_bar(bar);
    861 	else
    862 		hide_bar(bar);
    863 }
    864 
    865 static void
    866 dwl_wm_output_active(void *data, struct zdwl_ipc_output_v2 *dwl_wm_output,
    867 	uint32_t active)
    868 {
    869 	Bar *bar = (Bar *)data;
    870 
    871 	if (active != bar->sel)
    872 		bar->sel = active;
    873 }
    874 
    875 static void
    876 dwl_wm_output_tag(void *data, struct zdwl_ipc_output_v2 *dwl_wm_output,
    877 	uint32_t tag, uint32_t state, uint32_t clients, uint32_t focused)
    878 {
    879 	Bar *bar = (Bar *)data;
    880 
    881 	if (state & ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE)
    882 		bar->mtags |= 1 << tag;
    883 	else
    884 		bar->mtags &= ~(1 << tag);
    885 	if (clients > 0)
    886 		bar->ctags |= 1 << tag;
    887 	else
    888 		bar->ctags &= ~(1 << tag);
    889 	if (state & ZDWL_IPC_OUTPUT_V2_TAG_STATE_URGENT)
    890 		bar->urg |= 1 << tag;
    891 	else
    892 		bar->urg &= ~(1 << tag);
    893 }
    894 
    895 static void
    896 dwl_wm_output_layout(void *data, struct zdwl_ipc_output_v2 *dwl_wm_output,
    897 	uint32_t layout)
    898 {
    899 	Bar *bar = (Bar *)data;
    900 
    901 	bar->last_layout_idx = bar->layout_idx;
    902 	bar->layout_idx = layout;
    903 }
    904 
    905 static void
    906 dwl_wm_output_title(void *data, struct zdwl_ipc_output_v2 *dwl_wm_output,
    907 	const char *title)
    908 {
    909 	if (custom_title)
    910 		return;
    911 
    912 	Bar *bar = (Bar *)data;
    913 
    914 	if (bar->window_title)
    915 		free(bar->window_title);
    916 	if (!(bar->window_title = strdup(title)))
    917 		EDIE("strdup");
    918 }
    919 
    920 static void
    921 dwl_wm_output_appid(void *data, struct zdwl_ipc_output_v2 *dwl_wm_output,
    922 	const char *appid)
    923 {
    924 }
    925 
    926 static void
    927 dwl_wm_output_layout_symbol(void *data, struct zdwl_ipc_output_v2 *dwl_wm_output,
    928 	const char *layout)
    929 {
    930 	Bar *bar = (Bar *)data;
    931 
    932 	if (layouts[bar->layout_idx])
    933 		free(layouts[bar->layout_idx]);
    934 	if (!(layouts[bar->layout_idx] = strdup(layout)))
    935 		EDIE("strdup");
    936 	bar->layout = layouts[bar->layout_idx];
    937 }
    938 
    939 static void
    940 dwl_wm_output_frame(void *data, struct zdwl_ipc_output_v2 *dwl_wm_output)
    941 {
    942 	Bar *bar = (Bar *)data;
    943 	bar->redraw = true;
    944 }
    945 
    946 static void
    947 dwl_wm_output_fullscreen(void *data, struct zdwl_ipc_output_v2 *dwl_wm_output,
    948 	uint32_t is_fullscreen)
    949 {
    950 }
    951 
    952 static void
    953 dwl_wm_output_floating(void *data, struct zdwl_ipc_output_v2 *dwl_wm_output,
    954 	uint32_t is_floating)
    955 {
    956 }
    957 
    958 static const struct zdwl_ipc_output_v2_listener dwl_wm_output_listener = {
    959 	.toggle_visibility = dwl_wm_output_toggle_visibility,
    960 	.active = dwl_wm_output_active,
    961 	.tag = dwl_wm_output_tag,
    962 	.layout = dwl_wm_output_layout,
    963 	.title = dwl_wm_output_title,
    964 	.appid = dwl_wm_output_appid,
    965 	.layout_symbol = dwl_wm_output_layout_symbol,
    966 	.frame = dwl_wm_output_frame,
    967 	.fullscreen = dwl_wm_output_fullscreen,
    968 	.floating = dwl_wm_output_floating
    969 };
    970 
    971 static void
    972 setup_bar(Bar *bar)
    973 {
    974 	bar->height = height * buffer_scale;
    975 	bar->textpadding = textpadding;
    976 	bar->bottom = bottom;
    977 	bar->hidden = hidden;
    978 
    979 	bar->xdg_output = zxdg_output_manager_v1_get_xdg_output(output_manager, bar->wl_output);
    980 	if (!bar->xdg_output)
    981 		DIE("Could not create xdg_output");
    982 	zxdg_output_v1_add_listener(bar->xdg_output, &output_listener, bar);
    983 
    984 	if (ipc) {
    985 		bar->dwl_wm_output = zdwl_ipc_manager_v2_get_output(dwl_wm, bar->wl_output);
    986 		if (!bar->dwl_wm_output)
    987 			DIE("Could not create dwl_wm_output");
    988 		zdwl_ipc_output_v2_add_listener(bar->dwl_wm_output, &dwl_wm_output_listener, bar);
    989 	}
    990 	
    991 	if (!bar->hidden)
    992 		show_bar(bar);
    993 }
    994 
    995 static void
    996 handle_global(void *data, struct wl_registry *registry,
    997 	      uint32_t name, const char *interface, uint32_t version)
    998 {
    999 	if (!strcmp(interface, wl_compositor_interface.name)) {
   1000 		compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4);
   1001 	} else if (!strcmp(interface, wl_shm_interface.name)) {
   1002 		shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
   1003 	} else if (!strcmp(interface, zwlr_layer_shell_v1_interface.name)) {
   1004 		layer_shell = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 1);
   1005 	} else if (!strcmp(interface, zxdg_output_manager_v1_interface.name)) {
   1006 		output_manager = wl_registry_bind(registry, name, &zxdg_output_manager_v1_interface, 2);
   1007 	} else if (!strcmp(interface, zdwl_ipc_manager_v2_interface.name)) {
   1008 		if (ipc) {
   1009 			dwl_wm = wl_registry_bind(registry, name, &zdwl_ipc_manager_v2_interface, 2);
   1010 			zdwl_ipc_manager_v2_add_listener(dwl_wm, &dwl_wm_listener, NULL);
   1011 		}
   1012 	} else if (!strcmp(interface, wl_output_interface.name)) {
   1013 		Bar *bar = calloc(1, sizeof(Bar));
   1014 		if (!bar)
   1015 			EDIE("calloc");
   1016 		bar->registry_name = name;
   1017 		bar->wl_output = wl_registry_bind(registry, name, &wl_output_interface, 1);
   1018 		if (run_display)
   1019 			setup_bar(bar);
   1020 		wl_list_insert(&bar_list, &bar->link);
   1021 	} else if (!strcmp(interface, wl_seat_interface.name)) {
   1022 		Seat *seat = calloc(1, sizeof(Seat));
   1023 		if (!seat)
   1024 			EDIE("calloc");
   1025 		seat->registry_name = name;
   1026 		seat->wl_seat = wl_registry_bind(registry, name, &wl_seat_interface, 7);
   1027 		wl_seat_add_listener(seat->wl_seat, &seat_listener, seat);
   1028 		wl_list_insert(&seat_list, &seat->link);
   1029 	}
   1030 }
   1031 
   1032 static void
   1033 teardown_bar(Bar *bar)
   1034 {
   1035 	if (bar->status.colors)
   1036 		free(bar->status.colors);
   1037 	if (bar->status.buttons)
   1038 		free(bar->status.buttons);
   1039 	if (bar->title.colors)
   1040 		free(bar->title.colors);
   1041 	if (bar->title.buttons)
   1042 		free(bar->title.buttons);
   1043 	if (bar->window_title)
   1044 		free(bar->window_title);
   1045 	if (!ipc && bar->layout)
   1046 		free(bar->layout);
   1047 	if (ipc)
   1048 		zdwl_ipc_output_v2_destroy(bar->dwl_wm_output);
   1049 	if (bar->xdg_output_name)
   1050 		free(bar->xdg_output_name);
   1051 	if (!bar->hidden) {
   1052 		zwlr_layer_surface_v1_destroy(bar->layer_surface);
   1053 		wl_surface_destroy(bar->wl_surface);
   1054 	}
   1055 	zxdg_output_v1_destroy(bar->xdg_output);
   1056 	wl_output_destroy(bar->wl_output);
   1057 	free(bar);
   1058 }
   1059 
   1060 static void
   1061 teardown_seat(Seat *seat)
   1062 {
   1063 	if (seat->wl_pointer)
   1064 		wl_pointer_destroy(seat->wl_pointer);
   1065 	wl_seat_destroy(seat->wl_seat);
   1066 	free(seat);
   1067 }
   1068 
   1069 static void
   1070 handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
   1071 {
   1072 	Bar *bar;
   1073 	Seat *seat;
   1074 	
   1075 	wl_list_for_each(bar, &bar_list, link) {
   1076 		if (bar->registry_name == name) {
   1077 			wl_list_remove(&bar->link);
   1078 			teardown_bar(bar);
   1079 			return;
   1080 		}
   1081 	}
   1082 	wl_list_for_each(seat, &seat_list, link) {
   1083 		if (seat->registry_name == name) {
   1084 			wl_list_remove(&seat->link);
   1085 			teardown_seat(seat);
   1086 			return;
   1087 		}
   1088 	}
   1089 }
   1090 
   1091 static const struct wl_registry_listener registry_listener = {
   1092 	.global = handle_global,
   1093 	.global_remove = handle_global_remove
   1094 };
   1095 
   1096 static int
   1097 advance_word(char **beg, char **end)
   1098 {
   1099 	for (*beg = *end; **beg == ' '; (*beg)++);
   1100 	for (*end = *beg; **end && **end != ' '; (*end)++);
   1101 	if (!**end)
   1102 		/* last word */
   1103 		return -1;
   1104 	**end = '\0';
   1105 	(*end)++;
   1106 	return 0;
   1107 }
   1108 
   1109 #define ADVANCE() advance_word(&wordbeg, &wordend)
   1110 #define ADVANCE_IF_LAST_CONT() if (ADVANCE() == -1) continue
   1111 #define ADVANCE_IF_LAST_RET() if (ADVANCE() == -1) return
   1112 
   1113 static void
   1114 read_stdin(void)
   1115 {
   1116 	size_t len = 0;
   1117 	for (;;) {
   1118 		ssize_t rv = read(STDIN_FILENO, stdinbuf + len, stdinbuf_cap - len);
   1119 		if (rv == -1) {
   1120 			if (errno == EWOULDBLOCK)
   1121 				break;
   1122 			EDIE("read");
   1123 		}
   1124 		if (rv == 0) {
   1125 			run_display = false;
   1126 			return;
   1127 		}
   1128 
   1129 		if ((len += rv) > stdinbuf_cap / 2)
   1130 			if (!(stdinbuf = realloc(stdinbuf, (stdinbuf_cap *= 2))))
   1131 				EDIE("realloc");
   1132 	}
   1133 
   1134 	char *linebeg, *lineend;
   1135 	char *wordbeg, *wordend;
   1136 	
   1137 	for (linebeg = stdinbuf;
   1138 	     (lineend = memchr(linebeg, '\n', stdinbuf + len - linebeg));
   1139 	     linebeg = lineend) {
   1140 		*lineend++ = '\0';
   1141 		wordend = linebeg;
   1142 
   1143 		ADVANCE_IF_LAST_CONT();
   1144 
   1145 		Bar *it, *bar = NULL;
   1146 		wl_list_for_each(it, &bar_list, link) {
   1147 			if (it->xdg_output_name && !strcmp(wordbeg, it->xdg_output_name)) {
   1148 				bar = it;
   1149 				break;
   1150 			}
   1151 		}
   1152 		if (!bar)
   1153 			continue;
   1154 		
   1155 		ADVANCE_IF_LAST_CONT();
   1156 
   1157 		uint32_t val;
   1158 		if (!strcmp(wordbeg, "tags")) {
   1159 			ADVANCE_IF_LAST_CONT();
   1160 			if ((val = atoi(wordbeg)) != bar->ctags) {
   1161 				bar->ctags = val;
   1162 				bar->redraw = true;
   1163 			}
   1164 			ADVANCE_IF_LAST_CONT();
   1165 			if ((val = atoi(wordbeg)) != bar->mtags) {
   1166 				bar->mtags = val;
   1167 				bar->redraw = true;
   1168 			}
   1169 			ADVANCE_IF_LAST_CONT();
   1170 			/* skip sel */
   1171 			ADVANCE();
   1172 			if ((val = atoi(wordbeg)) != bar->urg) {
   1173 				bar->urg = val;
   1174 				bar->redraw = true;
   1175 			}
   1176 		} else if (!strcmp(wordbeg, "layout")) {
   1177 			if (bar->layout)
   1178 				free(bar->layout);
   1179 			if (!(bar->layout = strdup(wordend)))
   1180 				EDIE("strdup");
   1181 			bar->redraw = true;
   1182 		} else if (!strcmp(wordbeg, "title")) {
   1183 			if (custom_title)
   1184 				continue;
   1185 			if (bar->window_title)
   1186 				free(bar->window_title);
   1187 			if (!(bar->window_title = strdup(wordend)))
   1188 				EDIE("strdup");
   1189 			bar->redraw = true;
   1190 		} else if (!strcmp(wordbeg, "selmon")) {
   1191 			ADVANCE();
   1192 			if ((val = atoi(wordbeg)) != bar->sel) {
   1193 				bar->sel = val;
   1194 				bar->redraw = true;
   1195 			}
   1196 		}
   1197 	}
   1198 }
   1199 
   1200 static void
   1201 set_top(Bar *bar)
   1202 {
   1203 	if (!bar->hidden) {
   1204 		zwlr_layer_surface_v1_set_anchor(bar->layer_surface,
   1205 						 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
   1206 						 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
   1207 						 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT);
   1208 		bar->redraw = true;
   1209 	}
   1210 	bar->bottom = false;
   1211 }
   1212 
   1213 static void
   1214 set_bottom(Bar *bar)
   1215 {
   1216 	if (!bar->hidden) {
   1217 		zwlr_layer_surface_v1_set_anchor(bar->layer_surface,
   1218 						 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM
   1219 						 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
   1220 						 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT);
   1221 		bar->redraw = true;
   1222 	}
   1223 	bar->bottom = true;
   1224 }
   1225 
   1226 /* Color parsing logic adapted from [sway] */
   1227 static int
   1228 parse_color(const char *str, pixman_color_t *clr)
   1229 {
   1230 	if (*str == '#')
   1231 		str++;
   1232 	int len = strlen(str);
   1233 
   1234 	// Disallows "0x" prefix that strtoul would ignore
   1235 	if ((len != 6 && len != 8) || !isxdigit(str[0]) || !isxdigit(str[1]))
   1236 		return -1;
   1237 
   1238 	char *ptr;
   1239 	uint32_t parsed = strtoul(str, &ptr, 16);
   1240 	if (*ptr)
   1241 		return -1;
   1242 
   1243 	if (len == 8) {
   1244 		clr->alpha = (parsed & 0xff) * 0x101;
   1245 		parsed >>= 8;
   1246 	} else {
   1247 		clr->alpha = 0xffff;
   1248 	}
   1249 	clr->red =   ((parsed >> 16) & 0xff) * 0x101;
   1250 	clr->green = ((parsed >>  8) & 0xff) * 0x101;
   1251 	clr->blue =  ((parsed >>  0) & 0xff) * 0x101;
   1252 	return 0;
   1253 }
   1254 
   1255 static void
   1256 parse_into_customtext(CustomText *ct, char *text)
   1257 {
   1258 	ct->colors_l = ct->buttons_l = 0;
   1259 
   1260 	if (status_commands) {
   1261 		uint32_t codepoint;
   1262 		uint32_t state = UTF8_ACCEPT;
   1263 		uint32_t last_cp = 0;
   1264 		uint32_t x = 0;
   1265 		size_t str_pos = 0;
   1266 
   1267 		Button *left_button = NULL;
   1268 		Button *middle_button = NULL;
   1269 		Button *right_button = NULL;
   1270 	
   1271 		for (char *p = text; *p && str_pos < sizeof(ct->text) - 1; p++) {
   1272 			if (state == UTF8_ACCEPT && *p == '^') {
   1273 				p++;
   1274 				if (*p != '^') {
   1275 					char *arg, *end;
   1276 					if (!(arg = strchr(p, '(')) || !(end = strchr(arg + 1, ')')))
   1277 						continue;
   1278 					*arg++ = '\0';
   1279 					*end = '\0';
   1280 				
   1281 					if (!strcmp(p, "bg")) {
   1282 						Color *color;
   1283 						ARRAY_APPEND(ct->colors, ct->colors_l, ct->colors_c, color);
   1284 						if (!*arg)
   1285 							color->color = inactive_bg_color;
   1286 						else
   1287 							parse_color(arg, &color->color);
   1288 						color->bg = true;
   1289 						color->start = ct->text + str_pos;
   1290 					} else if (!strcmp(p, "fg")) {
   1291 						Color *color;
   1292 						ARRAY_APPEND(ct->colors, ct->colors_l, ct->colors_c, color);
   1293 						if (!*arg)
   1294 							color->color = inactive_fg_color;
   1295 						else
   1296 							parse_color(arg, &color->color);
   1297 						color->bg = false;
   1298 						color->start = ct->text + str_pos;
   1299 					} else if (!strcmp(p, "lm")) {
   1300 						if (left_button) {
   1301 							left_button->x2 = x;
   1302 							left_button = NULL;
   1303 						} else if (*arg) {
   1304 							ARRAY_APPEND(ct->buttons, ct->buttons_l, ct->buttons_c, left_button);
   1305 							left_button->btn = BTN_LEFT;
   1306 							snprintf(left_button->command, sizeof left_button->command, "%s", arg);
   1307 							left_button->x1 = x;
   1308 						}
   1309 					} else if (!strcmp(p, "mm")) {
   1310 						if (middle_button) {
   1311 							middle_button->x2 = x;
   1312 							middle_button = NULL;
   1313 						} else if (*arg) {
   1314 							ARRAY_APPEND(ct->buttons, ct->buttons_l, ct->buttons_c, middle_button);
   1315 							middle_button->btn = BTN_MIDDLE;
   1316 							snprintf(middle_button->command, sizeof middle_button->command, "%s", arg);
   1317 							middle_button->x1 = x;
   1318 						}
   1319 					} else if (!strcmp(p, "rm")) {
   1320 						if (right_button) {
   1321 							right_button->x2 = x;
   1322 							right_button = NULL;
   1323 						} else if (*arg) {
   1324 							ARRAY_APPEND(ct->buttons, ct->buttons_l, ct->buttons_c, right_button);
   1325 							right_button->btn = BTN_RIGHT;
   1326 							snprintf(right_button->command, sizeof right_button->command, "%s", arg);
   1327 							right_button->x1 = x;
   1328 						}
   1329 					} 
   1330 
   1331 					*--arg = '(';
   1332 					*end = ')';
   1333 				
   1334 					p = end;
   1335 					continue;
   1336 				}
   1337 			}
   1338 
   1339 			ct->text[str_pos++] = *p;
   1340 			
   1341 			if (utf8decode(&state, &codepoint, *p))
   1342 				continue;
   1343 			
   1344 			const struct fcft_glyph *glyph = fcft_rasterize_char_utf32(font, codepoint, FCFT_SUBPIXEL_NONE);
   1345 			if (!glyph)
   1346 				continue;
   1347 				
   1348 			long kern = 0;
   1349 			if (last_cp)
   1350 				fcft_kerning(font, last_cp, codepoint, &kern, NULL);
   1351 			last_cp = codepoint;
   1352 			
   1353 			x += kern + glyph->advance.x;
   1354 		}
   1355 
   1356 		if (left_button)
   1357 			left_button->x2 = x;
   1358 		if (middle_button)
   1359 			middle_button->x2 = x;
   1360 		if (right_button)
   1361 			right_button->x2 = x;
   1362 		
   1363 		ct->text[str_pos] = '\0';
   1364 	} else {
   1365 		snprintf(ct->text, sizeof ct->text, "%s", text);
   1366 	}
   1367 }
   1368 
   1369 static void
   1370 copy_customtext(CustomText *from, CustomText *to)
   1371 {
   1372 	snprintf(to->text, sizeof to->text, "%s", from->text);
   1373 	to->colors_l = to->buttons_l = 0;
   1374 	for (uint32_t i = 0; i < from->colors_l; i++) {
   1375 		Color *color;
   1376 		ARRAY_APPEND(to->colors, to->colors_l, to->colors_c, color);
   1377 		color->color = from->colors[i].color;
   1378 		color->bg = from->colors[i].bg;
   1379 		color->start = from->colors[i].start - (char *)&from->text + (char *)&to->text;
   1380 	}
   1381 	for (uint32_t i = 0; i < from->buttons_l; i++) {
   1382 		Button *button;
   1383 		ARRAY_APPEND(to->buttons, to->buttons_l, to->buttons_c, button);
   1384 		*button = from->buttons[i];
   1385 	}
   1386 }
   1387 
   1388 static void
   1389 read_socket(void)
   1390 {
   1391 	int cli_fd;
   1392 	if ((cli_fd = accept(sock_fd, NULL, 0)) == -1)
   1393 		EDIE("accept");
   1394 	ssize_t len = recv(cli_fd, sockbuf, sizeof sockbuf - 1, 0);
   1395 	if (len == -1)
   1396 		EDIE("recv");
   1397 	close(cli_fd);
   1398 	if (len == 0)
   1399 		return;
   1400 	sockbuf[len] = '\0';
   1401 
   1402 	char *wordbeg, *wordend;
   1403 	wordend = (char *)&sockbuf;
   1404 
   1405 	ADVANCE_IF_LAST_RET();
   1406 		
   1407 	Bar *bar = NULL, *it;
   1408 	bool all = false;
   1409 		
   1410 	if (!strcmp(wordbeg, "all")) {
   1411 		all = true;
   1412 	} else if (!strcmp(wordbeg, "selected")) {
   1413 		wl_list_for_each(it, &bar_list, link) {
   1414 			if (it->sel) {
   1415 				bar = it;
   1416 				break;
   1417 			}
   1418 		}
   1419 	} else {
   1420 		wl_list_for_each(it, &bar_list, link) {
   1421 			if (it->xdg_output_name && !strcmp(wordbeg, it->xdg_output_name)) {
   1422 				bar = it;
   1423 				break;
   1424 			}
   1425 		}
   1426 	}
   1427 		
   1428 	if (!all && !bar)
   1429 		return;
   1430 	
   1431 	ADVANCE();
   1432 
   1433 	if (!strcmp(wordbeg, "status")) {
   1434 		if (!*wordend)
   1435 			return;
   1436 		if (all) {
   1437 			Bar *first = NULL;
   1438 			wl_list_for_each(bar, &bar_list, link) {
   1439 				if (first) {
   1440 					copy_customtext(&first->status, &bar->status);
   1441 				} else {
   1442 					parse_into_customtext(&bar->status, wordend);
   1443 					first = bar;
   1444 				}
   1445 				bar->redraw = true;
   1446 			}
   1447 		} else {
   1448 			parse_into_customtext(&bar->status, wordend);
   1449 			bar->redraw = true;
   1450 		}
   1451 	} else if (!strcmp(wordbeg, "title")) {
   1452 		if (!custom_title || !*wordend)
   1453 			return;
   1454 		if (all) {
   1455 			Bar *first = NULL;
   1456 			wl_list_for_each(bar, &bar_list, link) {
   1457 				if (first) {
   1458 					copy_customtext(&first->title, &bar->title);
   1459 				} else {
   1460 					parse_into_customtext(&bar->title, wordend);
   1461 					first = bar;
   1462 				}
   1463 				bar->redraw = true;
   1464 			}
   1465 		} else {
   1466 			parse_into_customtext(&bar->title, wordend);
   1467 			bar->redraw = true;
   1468 		}
   1469 	} else if (!strcmp(wordbeg, "show")) {
   1470 		if (all) {
   1471 			wl_list_for_each(bar, &bar_list, link)
   1472 				if (bar->hidden)
   1473 					show_bar(bar);
   1474 		} else {
   1475 			if (bar->hidden)
   1476 				show_bar(bar);
   1477 		}
   1478 	} else if (!strcmp(wordbeg, "hide")) {
   1479 		if (all) {
   1480 			wl_list_for_each(bar, &bar_list, link)
   1481 				if (!bar->hidden)
   1482 					hide_bar(bar);
   1483 		} else {
   1484 			if (!bar->hidden)
   1485 				hide_bar(bar);
   1486 		}
   1487 	} else if (!strcmp(wordbeg, "toggle-visibility")) {
   1488 		if (all) {
   1489 			wl_list_for_each(bar, &bar_list, link)
   1490 				if (bar->hidden)
   1491 					show_bar(bar);
   1492 				else
   1493 					hide_bar(bar);
   1494 		} else {
   1495 			if (bar->hidden)
   1496 				show_bar(bar);
   1497 			else
   1498 				hide_bar(bar);
   1499 		}
   1500 	} else if (!strcmp(wordbeg, "set-top")) {
   1501 		if (all) {
   1502 			wl_list_for_each(bar, &bar_list, link)
   1503 				if (bar->bottom)
   1504 					set_top(bar);
   1505 						
   1506 		} else {
   1507 			if (bar->bottom)
   1508 				set_top(bar);
   1509 		}
   1510 	} else if (!strcmp(wordbeg, "set-bottom")) {
   1511 		if (all) {
   1512 			wl_list_for_each(bar, &bar_list, link)
   1513 				if (!bar->bottom)
   1514 					set_bottom(bar);
   1515 						
   1516 		} else {
   1517 			if (!bar->bottom)
   1518 				set_bottom(bar);
   1519 		}
   1520 	} else if (!strcmp(wordbeg, "toggle-location")) {
   1521 		if (all) {
   1522 			wl_list_for_each(bar, &bar_list, link)
   1523 				if (bar->bottom)
   1524 					set_top(bar);
   1525 				else
   1526 					set_bottom(bar);
   1527 		} else {
   1528 			if (bar->bottom)
   1529 				set_top(bar);
   1530 			else
   1531 				set_bottom(bar);
   1532 		}
   1533 	}
   1534 }
   1535 
   1536 static void
   1537 event_loop(void)
   1538 {
   1539 	int wl_fd = wl_display_get_fd(display);
   1540 
   1541 	while (run_display) {
   1542 		fd_set rfds;
   1543 		FD_ZERO(&rfds);
   1544 		FD_SET(wl_fd, &rfds);
   1545 		FD_SET(sock_fd, &rfds);
   1546 		if (!ipc)
   1547 			FD_SET(STDIN_FILENO, &rfds);
   1548 
   1549 		wl_display_flush(display);
   1550 
   1551 		if (select(MAX(sock_fd, wl_fd) + 1, &rfds, NULL, NULL, NULL) == -1) {
   1552 			if (errno == EINTR)
   1553 				continue;
   1554 			else
   1555 				EDIE("select");
   1556 		}
   1557 		
   1558 		if (FD_ISSET(wl_fd, &rfds))
   1559 			if (wl_display_dispatch(display) == -1)
   1560 				break;
   1561 		if (FD_ISSET(sock_fd, &rfds))
   1562 			read_socket();
   1563 		if (!ipc && FD_ISSET(STDIN_FILENO, &rfds))
   1564 			read_stdin();
   1565 		
   1566 		Bar *bar;
   1567 		wl_list_for_each(bar, &bar_list, link) {
   1568 			if (bar->redraw) {
   1569 				if (!bar->hidden)
   1570 					draw_frame(bar);
   1571 				bar->redraw = false;
   1572 			}
   1573 		}
   1574 	}
   1575 }
   1576 
   1577 static void
   1578 client_send_command(struct sockaddr_un *sock_address, const char *output,
   1579 		    const char *cmd, const char *data, const char *target_socket)
   1580 {
   1581 	DIR *dir;
   1582 	if (!(dir = opendir(socketdir)))
   1583 		EDIE("Could not open directory '%s'", socketdir);
   1584 
   1585 	if (data)
   1586 		snprintf(sockbuf, sizeof sockbuf, "%s %s %s", output, cmd, data);
   1587 	else
   1588 		snprintf(sockbuf, sizeof sockbuf, "%s %s", output, cmd);
   1589 	
   1590 	size_t len = strlen(sockbuf);
   1591 			
   1592 	struct dirent *de;
   1593 	bool newfd = true;
   1594 
   1595 	/* Send data to all dwlb instances */
   1596 	while ((de = readdir(dir))) {
   1597 		if (!strncmp(de->d_name, "dwlb-", 5)) {
   1598 			if (!target_socket || !strncmp(de -> d_name, target_socket, 6)){
   1599 				if (newfd && (sock_fd = socket(AF_UNIX, SOCK_STREAM, 1)) == -1)
   1600 					EDIE("socket");
   1601 				snprintf(sock_address->sun_path, sizeof sock_address->sun_path, "%s/%s", socketdir, de->d_name);
   1602 				if (connect(sock_fd, (struct sockaddr *) sock_address, sizeof(*sock_address)) == -1) {
   1603 					newfd = false;
   1604 					continue;
   1605 				}
   1606 				if (send(sock_fd, sockbuf, len, 0) == -1)
   1607 					fprintf(stderr, "Could not send status data to '%s'\n", sock_address->sun_path);
   1608 				close(sock_fd);
   1609 				newfd = true;
   1610 			}
   1611 		}
   1612 	}
   1613 
   1614 	closedir(dir);
   1615 }
   1616 
   1617 void
   1618 sig_handler(int sig)
   1619 {
   1620 	if (sig == SIGINT || sig == SIGHUP || sig == SIGTERM)
   1621 		run_display = false;
   1622 }
   1623 
   1624 int
   1625 main(int argc, char **argv)
   1626 {
   1627 	char *xdgruntimedir;
   1628 	struct sockaddr_un sock_address;
   1629 	Bar *bar, *bar2;
   1630 	Seat *seat, *seat2;
   1631 
   1632 	/* Establish socket directory */
   1633 	if (!(xdgruntimedir = getenv("XDG_RUNTIME_DIR")))
   1634 		DIE("Could not retrieve XDG_RUNTIME_DIR");
   1635 	snprintf(socketdir, sizeof socketdir, "%s/dwlb", xdgruntimedir);
   1636 	if (mkdir(socketdir, S_IRWXU) == -1)
   1637 		if (errno != EEXIST)
   1638 			EDIE("Could not create directory '%s'", socketdir);
   1639 	sock_address.sun_family = AF_UNIX;
   1640 
   1641 	/* Parse options */
   1642 	char *target_socket = NULL;
   1643 	int i = 1;
   1644 	if (argc > 1 && !strcmp(argv[1], "-target-socket")) {
   1645 		if (2 >= argc) {
   1646 			DIE("Option -socket requires an argument");
   1647 		}
   1648 		target_socket = argv[2];
   1649 		i += 2;
   1650 	}
   1651 	for (; i < argc; i++) {
   1652 		if (!strcmp(argv[i], "-status")) {
   1653 			if (++i + 1 >= argc)
   1654 				DIE("Option -status requires two arguments");
   1655 			client_send_command(&sock_address, argv[i], "status", argv[i + 1], target_socket);
   1656 			return 0;
   1657 		} else if (!strcmp(argv[i], "-status-stdin")) {
   1658 			if (++i >= argc)
   1659 				DIE("Option -status-stdin requires an argument");
   1660 			char *status = malloc(TEXT_MAX * sizeof(char));
   1661 			while (fgets(status, TEXT_MAX-1, stdin)) {
   1662 				status[strlen(status)-1] = '\0';
   1663 				client_send_command(&sock_address, argv[i], "status", status, target_socket);
   1664 			}
   1665 			free(status);
   1666 			return 0;
   1667 		} else if (!strcmp(argv[i], "-title")) {
   1668 			if (++i + 1 >= argc)
   1669 				DIE("Option -title requires two arguments");
   1670 			client_send_command(&sock_address, argv[i], "title", argv[i + 1], target_socket);
   1671 			return 0;
   1672 		} else if (!strcmp(argv[i], "-show")) {
   1673 			if (++i >= argc)
   1674 				DIE("Option -show requires an argument");
   1675 			client_send_command(&sock_address, argv[i], "show", NULL, target_socket);
   1676 			return 0;
   1677 		} else if (!strcmp(argv[i], "-hide")) {
   1678 			if (++i >= argc)
   1679 				DIE("Option -hide requires an argument");
   1680 			client_send_command(&sock_address, argv[i], "hide", NULL, target_socket);
   1681 			return 0;
   1682 		} else if (!strcmp(argv[i], "-toggle-visibility")) {
   1683 			if (++i >= argc)
   1684 				DIE("Option -toggle requires an argument");
   1685 			client_send_command(&sock_address, argv[i], "toggle-visibility", NULL, target_socket);
   1686 			return 0;
   1687 		} else if (!strcmp(argv[i], "-set-top")) {
   1688 			if (++i >= argc)
   1689 				DIE("Option -set-top requires an argument");
   1690 			client_send_command(&sock_address, argv[i], "set-top", NULL, target_socket);
   1691 			return 0;
   1692 		} else if (!strcmp(argv[i], "-set-bottom")) {
   1693 			if (++i >= argc)
   1694 				DIE("Option -set-bottom requires an argument");
   1695 			client_send_command(&sock_address, argv[i], "set-bottom", NULL, target_socket);
   1696 			return 0;
   1697 		} else if (!strcmp(argv[i], "-toggle-location")) {
   1698 			if (++i >= argc)
   1699 				DIE("Option -toggle-location requires an argument");
   1700 			client_send_command(&sock_address, argv[i], "toggle-location", NULL, target_socket);
   1701 			return 0;
   1702 		} else if (!strcmp(argv[i], "-ipc")) {
   1703 			ipc = true;
   1704 		} else if (!strcmp(argv[i], "-no-ipc")) {
   1705 			ipc = false;
   1706 		} else if (!strcmp(argv[i], "-hide-vacant-tags")) {
   1707 			hide_vacant = true;
   1708 		} else if (!strcmp(argv[i], "-no-hide-vacant-tags")) {
   1709 			hide_vacant = false;
   1710 		} else if (!strcmp(argv[i], "-bottom")) {
   1711 			bottom = true;
   1712 		} else if (!strcmp(argv[i], "-no-bottom")) {
   1713 			bottom = false;
   1714 		} else if (!strcmp(argv[i], "-hidden")) {
   1715 			hidden = true;
   1716 		} else if (!strcmp(argv[i], "-no-hidden")) {
   1717 			hidden = false;
   1718 		} else if (!strcmp(argv[i], "-status-commands")) {
   1719 			status_commands = true;
   1720 		} else if (!strcmp(argv[i], "-no-status-commands")) {
   1721 			status_commands = false;
   1722 		} else if (!strcmp(argv[i], "-center-title")) {
   1723 			center_title = true;
   1724 		} else if (!strcmp(argv[i], "-no-center-title")) {
   1725 			center_title = false;
   1726 		} else if (!strcmp(argv[i], "-custom-title")) {
   1727 			custom_title = true;
   1728 		} else if (!strcmp(argv[i], "-no-custom-title")) {
   1729 			custom_title = false;
   1730 		} else if (!strcmp(argv[i], "-font")) {
   1731 			if (++i >= argc)
   1732 				DIE("Option -font requires an argument");
   1733 			fontstr = argv[i];
   1734 		} else if (!strcmp(argv[i], "-vertical-padding")) {
   1735 			if (++i >= argc)
   1736 				DIE("Option -vertical-padding requires an argument");
   1737 			vertical_padding = MAX(MIN(atoi(argv[i]), 100), 0);
   1738 		} else if (!strcmp(argv[i], "-active-fg-color")) {
   1739 			if (++i >= argc)
   1740 				DIE("Option -active-fg-color requires an argument");
   1741 			if (parse_color(argv[i], &active_fg_color) == -1)
   1742 				DIE("malformed color string");
   1743 		} else if (!strcmp(argv[i], "-active-bg-color")) {
   1744 			if (++i >= argc)
   1745 				DIE("Option -active-bg-color requires an argument");
   1746 			if (parse_color(argv[i], &active_bg_color) == -1)
   1747 				DIE("malformed color string");
   1748 		} else if (!strcmp(argv[i], "-occupied-fg-color")) {
   1749 			if (++i >= argc)
   1750 				DIE("Option -occupied-fg-color requires an argument");
   1751 			if (parse_color(argv[i], &occupied_fg_color) == -1)
   1752 				DIE("malformed color string");
   1753 		} else if (!strcmp(argv[i], "-occupied-bg-color")) {
   1754 			if (++i >= argc)
   1755 				DIE("Option -occupied-bg-color requires an argument");
   1756 			if (parse_color(argv[i], &occupied_bg_color) == -1)
   1757 				DIE("malformed color string");
   1758 		} else if (!strcmp(argv[i], "-inactive-fg-color")) {
   1759 			if (++i >= argc)
   1760 				DIE("Option -inactive-fg-color requires an argument");
   1761 			if (parse_color(argv[i], &inactive_fg_color) == -1)
   1762 				DIE("malformed color string");
   1763 		} else if (!strcmp(argv[i], "-inactive-bg-color")) {
   1764 			if (++i >= argc)
   1765 				DIE("Option -inactive-bg-color requires an argument");
   1766 			if (parse_color(argv[i], &inactive_bg_color) == -1)
   1767 				DIE("malformed color string");
   1768 		} else if (!strcmp(argv[i], "-urgent-fg-color")) {
   1769 			if (++i >= argc)
   1770 				DIE("Option -urgent-fg-color requires an argument");
   1771 			if (parse_color(argv[i], &urgent_fg_color) == -1)
   1772 				DIE("malformed color string");
   1773 		} else if (!strcmp(argv[i], "-urgent-bg-color")) {
   1774 			if (++i >= argc)
   1775 				DIE("Option -urgent-bg-color requires an argument");
   1776 			if (parse_color(argv[i], &urgent_bg_color) == -1)
   1777 				DIE("malformed color string");
   1778 		} else if (!strcmp(argv[i], "-middle-bg-color-selected")) {
   1779 			if (++i >= argc)
   1780 				DIE("Option -middle-bg-color-selected requires an argument");
   1781 			if (parse_color(argv[i], &middle_bg_color_selected) == -1)
   1782 				DIE("malformed color string");
   1783 		} else if (!strcmp(argv[i], "-middle-bg-color")) {
   1784 			if (++i >= argc)
   1785 				DIE("Option -middle-bg-color requires an argument");
   1786 			if (parse_color(argv[i], &middle_bg_color) == -1)
   1787 				DIE("malformed color string");
   1788 		} else if (!strcmp(argv[i], "-tags")) {
   1789 			if (++i >= argc)
   1790 				DIE("Option -tags requires at least one argument");
   1791 			int v;
   1792 			if ((v = atoi(argv[i])) < 0 || i + v >= argc)
   1793 				DIE("-tags: invalid arguments");
   1794 			if (tags) {
   1795 				for (uint32_t j = 0; j < tags_l; j++)
   1796 					free(tags[j]);
   1797 				free(tags);
   1798 			}
   1799 			if (!(tags = malloc(v * sizeof(char *))))
   1800 				EDIE("malloc");
   1801 			for (int j = 0; j < v; j++)
   1802 				if (!(tags[j] = strdup(argv[i + 1 + j])))
   1803 					EDIE("strdup");
   1804 			tags_l = tags_c = v;
   1805 			i += v;
   1806 		} else if (!strcmp(argv[i], "-scale")) {
   1807 			if (++i >= argc)
   1808 				DIE("Option -scale requires an argument");
   1809 			buffer_scale = strtoul(argv[i], &argv[i] + strlen(argv[i]), 10);
   1810 		} else if (!strcmp(argv[i], "-v")) {
   1811 			fprintf(stderr, PROGRAM " " VERSION "\n");
   1812 			return 0;
   1813 		} else if (!strcmp(argv[i], "-h")) {
   1814 			fprintf(stderr, USAGE);
   1815 			return 0;
   1816 		} else {
   1817 			DIE("Option '%s' not recognized\n" USAGE, argv[i]);
   1818 		}
   1819 	}
   1820 
   1821 	/* Set up display and protocols */
   1822 	display = wl_display_connect(NULL);
   1823 	if (!display)
   1824 		DIE("Failed to create display");
   1825 
   1826 	wl_list_init(&bar_list);
   1827 	wl_list_init(&seat_list);
   1828 	
   1829 	struct wl_registry *registry = wl_display_get_registry(display);
   1830 	wl_registry_add_listener(registry, &registry_listener, NULL);
   1831 	wl_display_roundtrip(display);
   1832 	if (!compositor || !shm || !layer_shell || !output_manager || (ipc && !dwl_wm))
   1833 		DIE("Compositor does not support all needed protocols");
   1834 
   1835 	/* Load selected font */
   1836 	fcft_init(FCFT_LOG_COLORIZE_AUTO, 0, FCFT_LOG_CLASS_ERROR);
   1837 	fcft_set_scaling_filter(FCFT_SCALING_FILTER_LANCZOS3);
   1838 
   1839 	unsigned int dpi = 96 * buffer_scale;
   1840 	char buf[10];
   1841 	snprintf(buf, sizeof buf, "dpi=%u", dpi);
   1842 	if (!(font = fcft_from_name(1, (const char *[]) {fontstr}, buf)))
   1843 		DIE("Could not load font");
   1844 	textpadding = font->height / 2;
   1845 	height = font->height / buffer_scale + vertical_padding * 2;
   1846 
   1847 	/* Configure tag names */
   1848 	if (!ipc && !tags) {
   1849 		if (!(tags = malloc(LENGTH(tags_names) * sizeof(char *))))
   1850 			EDIE("malloc");
   1851 		tags_l = tags_c = LENGTH(tags_names);
   1852 		for (uint32_t i = 0; i < tags_l; i++)
   1853 			if (!(tags[i] = strdup(tags_names[i])))
   1854 				EDIE("strdup");
   1855 	}
   1856 	
   1857 	/* Setup bars */
   1858 	wl_list_for_each(bar, &bar_list, link)
   1859 		setup_bar(bar);
   1860 	wl_display_roundtrip(display);
   1861 
   1862 	if (!ipc) {
   1863 		/* Configure stdin */
   1864 		if (fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK) == -1)
   1865 			EDIE("fcntl");
   1866 	
   1867 		/* Allocate stdin buffer */
   1868 		if (!(stdinbuf = malloc(1024)))
   1869 			EDIE("malloc");
   1870 		stdinbuf_cap = 1024;
   1871 	}
   1872 
   1873 	/* Set up socket */
   1874 	bool found = false;
   1875 	for (uint32_t i = 0; i < 50; i++) {
   1876 		if ((sock_fd = socket(AF_UNIX, SOCK_STREAM, 1)) == -1)
   1877 			DIE("socket");
   1878 		snprintf(sock_address.sun_path, sizeof sock_address.sun_path, "%s/dwlb-%i", socketdir, i);
   1879 		if (connect(sock_fd, (struct sockaddr *)&sock_address, sizeof sock_address) == -1) {
   1880 			found = true;
   1881 			break;
   1882 		}
   1883 		close(sock_fd);
   1884 	}
   1885 	if (!found)
   1886 		DIE("Could not secure a socket path");
   1887 
   1888 	socketpath = (char *)&sock_address.sun_path;
   1889 	unlink(socketpath);
   1890 	if (bind(sock_fd, (struct sockaddr *)&sock_address, sizeof sock_address) == -1)
   1891 		EDIE("bind");
   1892 	if (listen(sock_fd, SOMAXCONN) == -1)
   1893 		EDIE("listen");
   1894 	fcntl(sock_fd, F_SETFD, FD_CLOEXEC | fcntl(sock_fd, F_GETFD));
   1895 
   1896 	/* Set up signals */
   1897 	signal(SIGINT, sig_handler);
   1898 	signal(SIGHUP, sig_handler);
   1899 	signal(SIGTERM, sig_handler);
   1900 	signal(SIGCHLD, SIG_IGN);
   1901 	
   1902 	/* Run */
   1903 	run_display = true;
   1904 	event_loop();
   1905 
   1906 	/* Clean everything up */
   1907 	close(sock_fd);
   1908 	unlink(socketpath);
   1909 	
   1910 	if (!ipc)
   1911 		free(stdinbuf);
   1912 
   1913 	if (tags) {
   1914 		for (uint32_t i = 0; i < tags_l; i++)
   1915 			free(tags[i]);
   1916 		free(tags);
   1917 	}
   1918 	if (layouts) {
   1919 		for (uint32_t i = 0; i < layouts_l; i++)
   1920 			free(layouts[i]);
   1921 		free(layouts);
   1922 	}
   1923 
   1924 	wl_list_for_each_safe(bar, bar2, &bar_list, link)
   1925 		teardown_bar(bar);
   1926 	wl_list_for_each_safe(seat, seat2, &seat_list, link)
   1927 		teardown_seat(seat);
   1928 	
   1929 	zwlr_layer_shell_v1_destroy(layer_shell);
   1930 	zxdg_output_manager_v1_destroy(output_manager);
   1931 	if (ipc)
   1932 		zdwl_ipc_manager_v2_destroy(dwl_wm);
   1933 	
   1934 	fcft_destroy(font);
   1935 	fcft_fini();
   1936 	
   1937 	wl_shm_destroy(shm);
   1938 	wl_compositor_destroy(compositor);
   1939 	wl_registry_destroy(registry);
   1940 	wl_display_disconnect(display);
   1941 
   1942 	return 0;
   1943 }