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, ®istry_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 }