1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <string.h>
5#include <inttypes.h>
6#include <stdbool.h>
7#include <getopt.h>
8#include <signal.h>
9#include <errno.h>
10#include <fcntl.h>
11#include <poll.h>
12#include <time.h>
13#include <sys/wait.h>
14
15#include <wayland-client.h>
16#include <wld/wld.h>
17#include <wld/wayland.h>
18
19#include <scfg.h>
20
21#include "xdg-shell-protocol.h"
22#include "wlr-layer-shell-unstable-v1.h"
23
24#define MAXMOD 6
25
26#define LOG(...) fprintf(stderr, "panko: " __VA_ARGS__)
27#define ERRNUM(elem) \
28 LOG("at line %d: wrong number of values for option `%s`\n", elem.lineno, elem.name); return false
29#define ERRINC(elem) \
30 LOG("at line %d: incorrect value for option `%s`\n", elem.lineno, elem.name); return false
31
32enum side { SIDE_LEFT, SIDE_MIDDLE, SIDE_RIGHT };
33struct module {
34 char *name, *command;
35 uint32_t interval;
36 int32_t margins[4], padding[4];
37 uint32_t width, height;
38 struct {
39 uint32_t foreground, background;
40 char *font;
41 int border_width;
42 uint32_t border_color;
43 } style;
44
45 pid_t pid;
46 int fd;
47 char *buf;
48 size_t buflen;
49 uint64_t next;
50 struct wld_font *font;
51};
52
53struct config {
54 uint32_t width, height;
55 enum zwlr_layer_surface_v1_anchor anchor;
56 int32_t margins[4];
57 bool exclusive;
58 struct {
59 uint32_t foreground, background;
60 char *font;
61 } style;
62};
63
64struct panel {
65 struct module **parsed, *modules[3][MAXMOD];
66 size_t plen;
67};
68
69struct app {
70 struct wl_display *dpy;
71 struct wl_registry *reg;
72 struct wl_compositor *comp;
73 struct zwlr_layer_shell_v1 *layer_shell;
74
75 /* panel resources */
76 struct wl_surface *surface;
77 struct zwlr_layer_surface_v1 *layer_surface;
78
79 /* drawing resources */
80 struct wld_context *wld_ctx;
81 struct wld_renderer *wld_ren;
82 struct wld_font_context *wld_fctx;
83 struct wld_font *wld_font;
84 struct wld_surface *wld_surface;
85
86 struct config config;
87 struct panel panel;
88};
89
90static volatile sig_atomic_t running = 1;
91static char config_path[512];
92static bool have_config = true;
93
94static void
95handle_global(void *data, struct wl_registry *reg, uint32_t name,
96 const char *iface, uint32_t ver) {
97
98 struct app *app = data;
99
100 if (strcmp(iface, "wl_compositor") == 0) {
101 app->comp =
102 wl_registry_bind(reg, name, &wl_compositor_interface, ver < 4 ? ver : 4);
103 return;
104 }
105
106 if (strcmp(iface, "zwlr_layer_shell_v1") == 0) {
107 app->layer_shell =
108 wl_registry_bind(reg, name, &zwlr_layer_shell_v1_interface, 5);
109 return;
110 }
111}
112
113static void
114handle_global_remove(void *data, struct wl_registry *reg, uint32_t name) {
115 /* no-op */
116}
117
118static const struct wl_registry_listener reg_listener = {
119 .global = handle_global,
120 .global_remove = handle_global_remove
121};
122
123static void
124handle_panel_configure(void *data, struct zwlr_layer_surface_v1 *layer_surface,
125 uint32_t serial, uint32_t width, uint32_t height) {
126
127 /* ignored */
128}
129
130static void
131handle_panel_close(void *data, struct zwlr_layer_surface_v1 *layer_surface) {
132 zwlr_layer_surface_v1_destroy(layer_surface);
133}
134
135static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
136 .configure = handle_panel_configure,
137 .closed = handle_panel_close
138};
139
140static uint64_t
141now_ms(void) {
142 struct timespec ts;
143 clock_gettime(CLOCK_MONOTONIC, &ts);
144 return (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
145}
146
147static void
148module_add(struct app *app, struct module *m, enum side side) {
149 for (size_t i = 0; i < MAXMOD; i++) {
150 if (app->panel.modules[side][i] == NULL) {
151 app->panel.modules[side][i] = m;
152 return;
153 }
154 }
155 LOG("too many modules on the %s side\n",
156 (side == SIDE_LEFT) ? "left" : (side == SIDE_MIDDLE) ? "middle" : "right");
157}
158
159static bool
160module_run(struct module *m) {
161 if (!m->command)
162 return false;
163
164 int fds[2];
165 if (pipe(fds) < 0)
166 return false;
167
168 pid_t pid = fork();
169 if (pid < 0) {
170 close(fds[0]);
171 close(fds[1]);
172 return false;
173 }
174
175 if (pid == 0) {
176 dup2(fds[1], STDOUT_FILENO);
177 close(fds[0]);
178 close(fds[1]);
179 execl("/bin/sh", "sh", "-c", m->command, NULL);
180 _exit(127);
181 }
182
183 close(fds[1]);
184 m->pid = pid;
185 m->fd = fds[0];
186
187 int f = fcntl(m->fd, F_GETFL, 0);
188 if (f < 0 || fcntl(m->fd, F_SETFL, f | O_NONBLOCK) < 0) {
189 LOG("while running module `%s`: couldn't fcntl: %s\n", m->name, strerror(errno));
190 close(m->fd);
191 m->fd = -1;
192 kill(m->pid, SIGTERM);
193 m->pid = 0;
194 return false;
195 }
196
197 m->next = m->interval ? now_ms() + m->interval * 1000 : UINT64_MAX;
198 return true;
199}
200
201static void
202module_out(struct module *m) {
203 if (m->fd < 0)
204 return;
205 char buf[1024];
206 size_t total = 0;
207 ssize_t r = 0;
208 char *out = NULL;
209
210 while ((r = read(m->fd, buf, sizeof(buf))) > 0) {
211 char *n = realloc(out, total + r + 1);
212 if (!n) {
213 free(out);
214 out = NULL;
215 break;
216 }
217 out = n;
218 memcpy(out + total, buf, r);
219 total += r;
220 out[total] = '\0';
221 }
222
223 if (r <= 0) {
224 if (errno == EAGAIN || errno == EWOULDBLOCK)
225 ; /* non-problem */
226 else
227 LOG("while running module `%s`: couldn't read: %s\n", m->name, strerror(errno));
228 int ret;
229 waitpid(m->pid, &ret, WNOHANG);
230 close(m->fd);
231 m->pid = 0;
232 m->fd = -1;
233 m->next = m->interval ? now_ms() + m->interval * 1000 : UINT64_MAX;
234 }
235
236 if (out) {
237 while (total > 0 && (out[total-1] == '\n' || out[total-1] == '\r'))
238 out[--total] = '\0';
239 free(m->buf);
240 m->buf = out;
241 m->buflen = total;
242 } else {
243 free(m->buf);
244 m->buf = strdup("");
245 m->buflen = 0;
246 }
247}
248
249static bool
250parse_config(const char *path, struct app *app) {
251 struct scfg_block block;
252
253 int ret = scfg_load_file(&block, path);
254 if (ret < 0) {
255 LOG("couldn't load config file: %s\n", strerror(-ret));
256 return false;
257 }
258
259 for (size_t i = 0; i < block.directives_len; i++) {
260 struct scfg_directive d = block.directives[i];
261
262 if (strcmp(d.name, "width") == 0 || strcmp(d.name, "height") == 0) {
263 if (d.params_len != 1) {
264 ERRNUM(d);
265 }
266 uint32_t *k = d.name[0] == 'w' ? &app->config.width : &app->config.height;
267 *k = strtoul(d.params[0], NULL, 0);
268 }
269
270 else if (strcmp(d.name, "anchor") == 0) {
271 if (d.params_len > 4 || d.params_len <= 0) {
272 ERRNUM(d);
273 }
274 for (size_t j = 0; j < d.params_len; j++) {
275 if (strcmp(d.params[j], "top") == 0)
276 app->config.anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
277 else if (strcmp(d.params[j], "right") == 0)
278 app->config.anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
279 else if (strcmp(d.params[j], "bottom") == 0)
280 app->config.anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
281 else if (strcmp(d.params[j], "left") == 0)
282 app->config.anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT;
283 else {
284 ERRINC(d);
285 }
286 }
287 }
288
289 else if (strcmp(d.name, "margins") == 0) {
290 if (d.params_len != 4) {
291 ERRNUM(d);
292 }
293 for (int j = 0; j < 4; j++)
294 app->config.margins[j] = strtol(d.params[j], NULL, 0);
295 }
296
297 else if (strcmp(d.name, "exclusive") == 0) {
298 if (d.params_len != 1) {
299 ERRNUM(d);
300 }
301 if (strcmp(d.params[0], "true") == 0)
302 app->config.exclusive = true;
303 else if (strcmp(d.params[0], "false") == 0)
304 app->config.exclusive = false;
305 else {
306 ERRINC(d);
307 }
308 }
309
310 else if (strcmp(d.name, "style") == 0) {
311 struct scfg_block sub = d.children;
312 for (size_t j = 0; j < sub.directives_len; j++) {
313 struct scfg_directive s = sub.directives[j];
314
315 if (strcmp(s.name, "foreground") == 0) {
316 if (s.params_len != 1) {
317 ERRNUM(s);
318 }
319 wld_lookup_named_color(s.params[0], &app->config.style.foreground);
320 }
321
322 else if (strcmp(s.name, "background") == 0) {
323 if (s.params_len != 1) {
324 ERRNUM(s);
325 }
326 wld_lookup_named_color(s.params[0], &app->config.style.background);
327 }
328
329 else if (strcmp(s.name, "font") == 0) {
330 if (s.params_len != 1) {
331 ERRNUM(s);
332 }
333 app->config.style.font = strdup(s.params[0]);
334 }
335 }
336 }
337
338 else if (strcmp(d.name, "module") == 0) {
339 if (d.params_len != 1) {
340 ERRNUM(d);
341 }
342 struct module m = {0};
343 memset(&m, 0, sizeof(m));
344 m.name = strdup(d.params[0]);
345
346 m.style.foreground = 0xDDDDDD;
347 m.style.background = 0x121212;
348 m.style.font = strdup(app->config.style.font);
349 m.style.border_width = 2;
350 m.style.border_color = 0x242424;
351
352 struct scfg_block sub = d.children;
353 for (size_t j = 0; j < sub.directives_len; j++) {
354 struct scfg_directive s = sub.directives[j];
355
356 if (strcmp(s.name, "command") == 0) {
357 if (s.params_len != 1) {
358 ERRNUM(s);
359 }
360 m.command = strdup(s.params[0]);
361 }
362
363 else if (strcmp(s.name, "interval") == 0) {
364 if (s.params_len != 1) {
365 ERRNUM(s);
366 }
367 m.interval = strtoul(s.params[0], NULL, 0);
368 }
369
370 else if (strcmp(s.name, "margins") == 0 || strcmp(s.name, "padding") == 0) {
371 if (s.params_len != 4) {
372 ERRNUM(s);
373 }
374 int32_t *k = s.name[0] == 'm' ? m.margins : m.padding;
375 for (int y = 0; y < 4; y++)
376 k[y] = strtol(s.params[y], NULL, 0);
377 }
378
379 else if (strcmp(s.name, "width") == 0 || strcmp(s.name, "height") == 0) {
380 if (s.params_len != 1) {
381 ERRNUM(s);
382 }
383 uint32_t *k = s.name[0] == 'w' ? &m.width : &m.height;
384 *k = strtoul(s.params[0], NULL, 0);
385 }
386
387 else if (strcmp(s.name, "style") == 0) {
388 struct scfg_block ssub = s.children;
389 for (size_t n = 0; n < ssub.directives_len; n++) {
390 struct scfg_directive t = ssub.directives[n];
391
392 if (strcmp(t.name, "background") == 0) {
393 if (t.params_len != 1) {
394 ERRNUM(t);
395 }
396 wld_lookup_named_color(t.params[0], &m.style.background);
397 }
398
399 else if (strcmp(t.name, "foreground") == 0) {
400 if (t.params_len != 1) {
401 ERRNUM(t);
402 }
403 wld_lookup_named_color(t.params[0], &m.style.foreground);
404 }
405
406 else if (strcmp(t.name, "font") == 0) {
407 if (t.params_len != 1) {
408 ERRNUM(t);
409 }
410 m.style.font = strdup(t.params[0]);
411 }
412
413 else if (strcmp(t.name, "border-width") == 0) {
414 if (t.params_len != 1) {
415 ERRNUM(t);
416 }
417 m.style.border_width = atoi(t.params[0]);
418 }
419
420 else if (strcmp(t.name, "border-color") == 0) {
421 if (t.params_len != 1) {
422 ERRNUM(t);
423 }
424 wld_lookup_named_color(t.params[0], &m.style.border_color);
425 }
426 }
427 }
428 }
429
430 struct module *new = calloc(1, sizeof(*new));
431 if (!new) {
432 LOG("couldn't allocate\n");
433 if (m.name) free(m.name);
434 if (m.command) free(m.command);
435 if (m.style.font) free(m.style.font);
436 scfg_block_finish(&block);
437 return false;
438 }
439 *new = m;
440
441 app->panel.parsed = realloc(app->panel.parsed, (app->panel.plen + 1) * sizeof(*app->panel.parsed));
442 if (!app->panel.parsed) {
443 LOG("couldn't allocate\n");
444 free(new);
445 if (m.name) free(m.name);
446 if (m.command) free(m.command);
447 if (m.style.font) free(m.style.font);
448 scfg_block_finish(&block);
449 return false;
450 }
451 app->panel.parsed[app->panel.plen++] = new;
452 }
453
454 else if (strcmp(d.name, "modules-left") == 0 || strcmp(d.name, "modules-middle") == 0 || strcmp(d.name, "modules-right") == 0) {
455 if (d.params_len < 0) {
456 ERRNUM(d);
457 }
458 enum side side = (d.name[8] == 'l') ? SIDE_LEFT : (d.name[8] == 'm') ? SIDE_MIDDLE : SIDE_RIGHT;
459 for (size_t p = 0; p < d.params_len; p++) {
460 const char *name = d.params[p];
461 if (name[0] == '\0')
462 continue;
463
464 struct module *m = NULL;
465 for (size_t i = 0; i < app->panel.plen; i++)
466 if (strcmp(app->panel.parsed[i]->name, name) == 0) {
467 m = app->panel.parsed[i];
468 break;
469 }
470
471 if (m) module_add(app, m, side);
472 else
473 LOG("module `%s` referenced but not defined\n", name);
474 }
475 }
476 }
477
478 scfg_block_finish(&block);
479 return true;
480}
481
482static void
483sig_handler(int s) {
484 running = 0;
485}
486
487static bool
488setup(struct app *app) {
489 memset(app, 0, sizeof(*app));
490
491 struct sigaction sa = {0};
492 sa.sa_handler = sig_handler;
493 sigemptyset(&sa.sa_mask);
494 sa.sa_flags = 0;
495 sigaction(SIGINT, &sa, NULL);
496 sigaction(SIGTERM, &sa, NULL);
497
498 struct sigaction sa_chld = {0};
499 sa_chld.sa_handler = SIG_IGN;
500 sa_chld.sa_flags = SA_NOCLDWAIT;
501 sigaction(SIGCHLD, &sa_chld, NULL);
502
503 app->dpy = wl_display_connect(NULL);
504 if (!app->dpy) {
505 LOG("couldn't connect to Wayland\n");
506 return false;
507 }
508
509 app->reg = wl_display_get_registry(app->dpy);
510 if (!app->reg) {
511 LOG("couldn't get registry\n");
512 return false;
513 }
514
515 wl_registry_add_listener(app->reg, ®_listener, app);
516 wl_display_roundtrip(app->dpy);
517
518 if (!app->comp || !app->layer_shell) {
519 LOG("required globals wl_compositor or layer_shell not available\n");
520 return false;
521 }
522
523 app->surface = wl_compositor_create_surface(app->comp);
524 if (!app->surface) {
525 LOG("couldn't create surface\n");
526 return false;
527 }
528 app->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
529 app->layer_shell,
530 app->surface,
531 NULL,
532 ZWLR_LAYER_SHELL_V1_LAYER_TOP,
533 "panko"
534 );
535
536 if (!app->layer_surface) {
537 LOG("couldn't get layer surface\n");
538 return false;
539 }
540
541 zwlr_layer_surface_v1_add_listener(
542 app->layer_surface,
543 &layer_surface_listener,
544 NULL
545 );
546
547 app->config.style.foreground = 0xDDDDDD;
548 app->config.style.background = 0x080808;
549 app->config.style.font = strdup("monospace:size=12");
550
551 if (config_path[0] == '\0') {
552 char *xdg_home = getenv("XDG_CONFIG_HOME");
553 if (xdg_home != NULL) {
554 snprintf(config_path, sizeof(config_path), "%s/panko.scfg", xdg_home);
555 }
556 else {
557 char *home = getenv("HOME");
558 if (home == NULL) {
559 LOG("couldn't find config path\n");
560 return false;
561 }
562 snprintf(config_path, sizeof(config_path), "%s/.config/panko/panko.scfg", home);
563 }
564 }
565
566 if (!parse_config(config_path, app))
567 return false;
568
569 zwlr_layer_surface_v1_set_size(
570 app->layer_surface,
571 app->config.width, app->config.height
572 );
573 zwlr_layer_surface_v1_set_anchor(
574 app->layer_surface,
575 app->config.anchor
576 );
577 zwlr_layer_surface_v1_set_exclusive_zone(
578 app->layer_surface,
579 app->config.exclusive ? app->config.height : 0
580 );
581 zwlr_layer_surface_v1_set_margin(
582 app->layer_surface,
583 app->config.margins[0],
584 app->config.margins[1],
585 app->config.margins[2],
586 app->config.margins[3]
587 );
588 wl_surface_commit(app->surface);
589 wl_display_roundtrip(app->dpy);
590
591 app->wld_ctx = wld_wayland_create_context(app->dpy, WLD_ANY);
592 if (!app->wld_ctx) {
593 LOG("couldn't create WLD context\n");
594 return false;
595 }
596
597 app->wld_ren = wld_create_renderer(app->wld_ctx);
598 if (!app->wld_ren) {
599 LOG("couldn't create WLD renderer\n");
600 return false;
601 }
602
603 app->wld_fctx = wld_font_create_context();
604 if (!app->wld_fctx) {
605 LOG("couldn't create WLD font context\n");
606 return false;
607 }
608
609 app->wld_font = wld_font_open_name(app->wld_fctx, app->config.style.font);
610 if (!app->wld_font) {
611 LOG("couldn't open font `%s`\n", app->config.style.font);
612 return false;
613 }
614
615 app->wld_surface =
616 wld_wayland_create_surface(app->wld_ctx, app->config.width,
617 app->config.height, WLD_FORMAT_XRGB8888, 0, app->surface);
618 if (!app->wld_surface) {
619 LOG("couldn't create WLD surface\n");
620 return false;
621 }
622
623 for (size_t i = 0; i < app->panel.plen; i++) {
624 struct module *m = app->panel.parsed[i];
625 m->fd = -1;
626 m->pid = 0;
627 m->buf = strdup("");
628 m->buflen = 0;
629 m->next = 0;
630
631 if (!module_run(m))
632 LOG("failed to start module `%s`\n", m->name);
633 }
634
635 for (int s = 0; s < 3; s++) {
636 for (size_t i = 0; i < MAXMOD; i++) {
637 struct module *m = app->panel.modules[s][i];
638 if (!m || !m->style.font || m->font)
639 continue;
640
641 m->font = wld_font_open_name(app->wld_fctx, m->style.font);
642 if (!m->font) {
643 LOG("couldn't open font `%s`\n", app->config.style.font);
644 return false;
645 }
646 }
647 }
648
649 return true;
650}
651
652static void
653module_draw(struct app *app, struct module *m, struct wld_font *f, uint32_t x) {
654 struct wld_extents ext;
655 wld_font_text_extents(f, m->buf, &ext);
656
657 uint32_t tw = (uint32_t)ext.advance;
658 uint32_t th = f->height;
659
660 uint32_t mw = m->width ? m->width : tw + m->padding[1] + m->padding[3];
661 uint32_t mh = m->height ? m->height : th + m->padding[0] + m->padding[2];
662 int32_t mx = x + m->margins[3];
663 int32_t my = app->config.height / 2 + m->margins[0] - (int32_t)mh / 2;
664
665 int32_t cw = mw - m->padding[1] - m->padding[3];
666 int32_t ch = mh - m->padding[0] - m->padding[2];
667 int32_t cx = mx + m->padding[3];
668 int32_t cy = my + m->padding[0];
669
670 uint32_t tx = cx + (cw - tw) / 2;
671 int32_t ty = cy + ch / 2 + th / 2 - f->descent;
672
673 wld_fill_rectangle(app->wld_ren, m->style.border_color,
674 mx - m->style.border_width, my - m->style.border_width,
675 mw + m->style.border_width * 2, mh + m->style.border_width * 2);
676 wld_fill_rectangle(app->wld_ren, m->style.background,
677 mx, my, mw, mh);
678 wld_draw_text(app->wld_ren, f, m->style.foreground,
679 tx, ty, m->buf, m->buflen, NULL);
680}
681
682static void
683draw(struct app *app) {
684 wld_set_target_surface(app->wld_ren, app->wld_surface);
685 wld_fill_rectangle(app->wld_ren, app->config.style.background,
686 0, 0, app->config.width, app->config.height);
687
688 /* left */
689 {
690 int32_t x = 0;
691
692 for (size_t i = 0; i < MAXMOD; i++) {
693 struct module *m = app->panel.modules[SIDE_LEFT][i];
694 if (!m || m->buflen == 0)
695 continue;
696
697 struct wld_font *font = m->font ? m->font : app->wld_font;
698 module_draw(app, m, font, x);
699
700 struct wld_extents ext;
701 wld_font_text_extents(font, m->buf, &ext);
702 uint32_t w = m->width ? m->width : ext.advance + m->padding[1] + m->padding[3];
703
704 x += w;
705 }
706 }
707
708 /* middle
709 * I would love to have a better way to do this, but I don't know how */
710 {
711 uint32_t tw = 0;
712
713 for (size_t i = 0; i < MAXMOD; i++) {
714 struct module *m = app->panel.modules[SIDE_MIDDLE][i];
715 if (!m || m->buflen == 0)
716 continue;
717
718 struct wld_font *font = m->font ? m->font : app->wld_font;
719
720 struct wld_extents ext;
721 wld_font_text_extents(font, m->buf, &ext);
722 uint32_t w = m->width ? m->width : ext.advance + m->padding[1] + m->padding[3];
723
724 tw += w;
725 }
726
727 int32_t x = app->config.width / 2 - tw / 2;
728
729 for (size_t i = 0; i < MAXMOD; i++) {
730 struct module *m = app->panel.modules[SIDE_MIDDLE][i];
731 if (!m || m->buflen == 0)
732 continue;
733
734 struct wld_font *font = m->font ? m->font : app->wld_font;
735 module_draw(app, m, font, x);
736
737 struct wld_extents ext;
738 wld_font_text_extents(font, m->buf, &ext);
739 uint32_t w = m->width ? m->width : ext.advance + m->padding[1] + m->padding[3];
740
741 x += w;
742 }
743 }
744
745 /* right */
746 {
747 int32_t x = app->config.width;
748
749 for (size_t i = MAXMOD; i > 0; --i) {
750 struct module *m = app->panel.modules[SIDE_RIGHT][i - 1];
751 if (!m || m->buflen == 0)
752 continue;
753
754 struct wld_font *font = m->font ? m->font : app->wld_font;
755
756 struct wld_extents ext;
757 wld_font_text_extents(font, m->buf, &ext);
758 uint32_t w = m->width ? m->width : ext.advance + m->padding[1] + m->padding[3];
759
760 x -= w + m->margins[1];
761 module_draw(app, m, font, x);
762 }
763 }
764
765 wl_surface_damage(app->surface, 0, 0, app->config.width, app->config.height);
766 wld_flush(app->wld_ren);
767 wld_swap(app->wld_surface);
768 wl_display_roundtrip(app->dpy);
769}
770
771static void
772run(struct app *app) {
773 while (running) {
774 uint64_t now = now_ms();
775 int timeout = 50, count = 0;
776
777 for (size_t i = 0; i < app->panel.plen; i++) {
778 struct module *m = app->panel.parsed[i];
779 if (m->next != UINT64_MAX) {
780 int64_t t = (int64_t)(m->next - now);
781 if (timeout < 0 || t < timeout)
782 timeout = (int)t;
783 }
784 if (m->fd >= 0)
785 count++;
786 }
787
788 int fds = 1 + count;
789 struct pollfd *pfds = calloc(fds, sizeof(*pfds));
790 if (!pfds)
791 return;
792
793 int n = 0;
794 pfds[n].fd = wl_display_get_fd(app->dpy);
795 pfds[n].events = POLLIN;
796 n++;
797
798 for (size_t i = 0; i < app->panel.plen; i++) {
799 struct module *m = app->panel.parsed[i];
800 if (m->fd >= 0) {
801 pfds[n].fd = m->fd;
802 pfds[n].events = POLLIN;
803 n++;
804 }
805 }
806
807 wl_display_dispatch_pending(app->dpy);
808 if (wl_display_flush(app->dpy) < 0 && errno != EAGAIN) {
809 free(pfds);
810 break;
811 }
812
813 int ret = poll(pfds, n, timeout);
814 now = now_ms();
815
816 if (ret < 0) {
817 if (errno == EINTR) {
818 free(pfds);
819 continue;
820 }
821 free(pfds);
822 break;
823 }
824
825 if (ret == 0) {
826 for (size_t i = 0; i < app->panel.plen; i++) {
827 struct module *m = app->panel.parsed[i];
828 if (m->fd < 0 && m->pid == 0 && m->interval && now >= m->next)
829 module_run(m);
830 }
831 free(pfds);
832 continue;
833 }
834
835 if (pfds[0].revents & POLLIN)
836 wl_display_dispatch(app->dpy);
837
838 bool redraw = false;
839 n = 1;
840 for (int s = 0; s < 3; s++) {
841 for (size_t i = 0; i < MAXMOD; i++) {
842 struct module *m = app->panel.modules[s][i];
843 if (!m)
844 continue;
845
846 if (m->fd >= 0) {
847 if (pfds[n].revents & (POLLIN | POLLHUP | POLLRDHUP)) {
848 module_out(m);
849 redraw = true;
850 }
851 n++;
852 }
853
854 if (m->fd < 0 && m->pid == 0 && m->interval && now >= m->next)
855 module_run(m);
856 }
857 }
858
859 if (redraw)
860 draw(app);
861
862 free(pfds);
863 }
864}
865
866static void
867cleanup(struct app *app) {
868 if (app->config.style.font)
869 free(app->config.style.font);
870 if (app->panel.parsed) {
871 for (size_t i = 0; i < app->panel.plen; i++) {
872 struct module *m = app->panel.parsed[i];
873 if (!m) continue;
874 if (m->pid > 0)
875 kill(m->pid, SIGTERM);
876 if (m->fd >= 0)
877 close(m->fd);
878 if (m->buf)
879 free(m->buf);
880 if (m->name)
881 free(m->name);
882 if (m->command)
883 free(m->command);
884 if (m->font)
885 wld_font_close(m->font);
886 if (m->style.font)
887 free(m->style.font);
888 free(m);
889 }
890 free(app->panel.parsed);
891 app->panel.parsed = NULL;
892 }
893 memset(app->panel.modules, 0, sizeof(app->panel.modules));
894
895 if (app->wld_surface)
896 wld_destroy_surface(app->wld_surface);
897 if (app->wld_font)
898 wld_font_close(app->wld_font);
899 if (app->wld_fctx)
900 wld_font_destroy_context(app->wld_fctx);
901 if (app->wld_ren)
902 wld_destroy_renderer(app->wld_ren);
903 if (app->wld_ctx)
904 wld_destroy_context(app->wld_ctx);
905 if (app->layer_surface)
906 zwlr_layer_surface_v1_destroy(app->layer_surface);
907 if (app->surface)
908 wl_surface_destroy(app->surface);
909 if (app->layer_shell)
910 zwlr_layer_shell_v1_destroy(app->layer_shell);
911 if (app->comp)
912 wl_compositor_destroy(app->comp);
913 if (app->reg)
914 wl_registry_destroy(app->reg);
915 if (app->dpy)
916 wl_display_disconnect(app->dpy);
917
918 memset(app, 0, sizeof(*app));
919}
920
921int
922main(int argc, char **argv) {
923 struct app app = {0};
924 config_path[0] = '\0';
925
926 int opt;
927 while ((opt = getopt(argc, argv, ":hvc:")) != -1) {
928 switch (opt) {
929 case 'h':
930 fprintf(stderr, "Usage: %s [-hv] [-c path]\n\n"
931 "Options:\n"
932 " -h\t\tPrint this help message\n"
933 " -v\t\tPrint the current version\n"
934 " -c path\tLoad the config from this path (default ~/.config/panko/panko.scfg)\n",
935 argv[0]
936 );
937 return 1;
938 break;
939 case 'v':
940 fprintf(stderr, "panko v" VERSION "\n");
941 return 0;
942 break;
943 case 'c':
944 snprintf(config_path, sizeof(config_path), "%s", optarg);
945 have_config = true;
946 break;
947 }
948 }
949
950 if (!setup(&app))
951 goto error;
952
953 run(&app);
954
955 cleanup(&app);
956 return 0;
957
958error:
959 cleanup(&app);
960 return 1;
961}