From c60f65195186e6c72ec66ba7f10139a420a595a0 Mon Sep 17 00:00:00 2001 From: Leonardo Hernández Hernández Date: Fri, 9 Dec 2022 08:46:22 -0600 Subject: Revert "force line-buffered stdout if stdout is not a tty" This reverts commit deb48ff48b186ff77a7e9d3b3ab724ff4c3c340f. Fixes: https://github.com/djpohly/dwl/issues/253 --- dwl.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dwl.c b/dwl.c index 9ee3c76..2f7f2bf 100644 --- a/dwl.c +++ b/dwl.c @@ -1803,6 +1803,7 @@ printstatus(void) sel, urg); printf("%s layout %s\n", m->wlr_output->name, m->lt[m->sellt]->symbol); } + fflush(stdout); } void @@ -2081,9 +2082,6 @@ setsel(struct wl_listener *listener, void *data) void setup(void) { - /* Force line-buffered stdout */ - setvbuf(stdout, NULL, _IOLBF, 0); - /* The Wayland display is managed by libwayland. It handles accepting * clients from the Unix socket, manging Wayland globals, and so on. */ dpy = wl_display_create(); -- cgit v1.2.3 From 94c8bd604870365bc201524b63158623e6f32a8f Mon Sep 17 00:00:00 2001 From: Leonardo Hernández Hernández Date: Fri, 9 Dec 2022 20:55:58 -0600 Subject: get `sel` from focustop() in focusstack() Fixes: https://github.com/djpohly/dwl/issues/354 --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 2f7f2bf..5add915 100644 --- a/dwl.c +++ b/dwl.c @@ -1261,7 +1261,7 @@ void focusstack(const Arg *arg) { /* Focus the next or previous client (in tiling order) on selmon */ - Client *c, *sel = selclient(); + Client *c, *sel = focustop(selmon); if (!sel || sel->isfullscreen) return; if (arg->i > 0) { -- cgit v1.2.3 From a39a46c908011de913b142dbd1dd427eb7fbca0a Mon Sep 17 00:00:00 2001 From: Ben Jargowsky Date: Thu, 15 Dec 2022 17:34:03 -0500 Subject: Check width and height are not negative in client_set_bounds() --- client.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client.h b/client.h index dbf018f..06c2073 100644 --- a/client.h +++ b/client.h @@ -141,7 +141,7 @@ client_set_bounds(Client *c, int32_t width, int32_t height) return 0; #endif if (c->surface.xdg->client->shell->version >= - XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION) + XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION && width >= 0 && height >= 0) return wlr_xdg_toplevel_set_bounds(c->surface.xdg->toplevel, width, height); return 0; } -- cgit v1.2.3 From 803a9ba98d6976c71e92973e3af6096476984fa3 Mon Sep 17 00:00:00 2001 From: Leonardo Hernández Hernández Date: Fri, 16 Dec 2022 16:31:31 -0600 Subject: Revert "Revert "Check that inhibitor scene tree is not null"" This reverts commit 035bb99d67b59a84cfc2e911d222fb597591a8be. Not checking `tree != NULL` result in a segfault if the surface doesn't have a role (for example because it is a newly created surface) Closes: https://github.com/djpohly/dwl/issues/359 --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 5add915..2bcb8b1 100644 --- a/dwl.c +++ b/dwl.c @@ -628,7 +628,7 @@ checkidleinhibitor(struct wlr_surface *exclude) struct wlr_surface *surface = wlr_surface_get_root_surface(inhibitor->surface); struct wlr_scene_tree *tree = surface->data; if (bypass_surface_visibility || (exclude != surface - && tree->node.enabled)) { + && tree && tree->node.enabled)) { inhibited = 1; break; } -- cgit v1.2.3 From 7b1fe7e5f2b92f025063cc98ebdb1791945e2f4a Mon Sep 17 00:00:00 2001 From: Leonardo Hernández Hernández Date: Fri, 16 Dec 2022 17:24:45 -0600 Subject: fix checking idle inhibit state checking `bypass_surface_visibility` first, could cause that even if the idle inhibitor is being destroyed it will disable idle tracking and if we couldn't get its scene tree, then assume that the surface is visible --- dwl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index 2bcb8b1..f535baf 100644 --- a/dwl.c +++ b/dwl.c @@ -627,8 +627,8 @@ checkidleinhibitor(struct wlr_surface *exclude) wl_list_for_each(inhibitor, &idle_inhibit_mgr->inhibitors, link) { struct wlr_surface *surface = wlr_surface_get_root_surface(inhibitor->surface); struct wlr_scene_tree *tree = surface->data; - if (bypass_surface_visibility || (exclude != surface - && tree && tree->node.enabled)) { + if (exclude != surface && (bypass_surface_visibility || (!tree + || tree->node.enabled))) { inhibited = 1; break; } -- cgit v1.2.3 From 1a3d89e5b2c14aa4aebe73a0a0804d14c30386ed Mon Sep 17 00:00:00 2001 From: Leonardo Hernández Hernández Date: Fri, 16 Dec 2022 18:54:33 -0600 Subject: call checkidleinhibitor() in arrange() and not in focusclient() --- dwl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index f535baf..6e3b034 100644 --- a/dwl.c +++ b/dwl.c @@ -477,6 +477,7 @@ arrange(Monitor *m) if (m && m->lt[m->sellt]->arrange) m->lt[m->sellt]->arrange(m); motionnotify(0); + checkidleinhibitor(NULL); } void @@ -1226,9 +1227,7 @@ focusclient(Client *c, int lift) client_activate_surface(old, 0); } } - printstatus(); - checkidleinhibitor(NULL); if (!c) { /* With no client, all we have left is to clear focus */ -- cgit v1.2.3 From 686958a4cc599fe212d8e1111999cd59a54e9ed6 Mon Sep 17 00:00:00 2001 From: Leonardo Hernández Hernández Date: Sat, 17 Dec 2022 13:46:04 -0600 Subject: fix unset fullscreen for all visible clients when mapping a new one this also changes our policy about when we unset fullscreen: dwl will unset fullscreen for clients who share tags (and monitor) with a newly mapped client, it does not matter if the clients are visible or not --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 6e3b034..4c045c2 100644 --- a/dwl.c +++ b/dwl.c @@ -1526,7 +1526,7 @@ mapnotify(struct wl_listener *listener, void *data) unset_fullscreen: m = c->mon ? c->mon : xytomon(c->geom.x, c->geom.y); wl_list_for_each(w, &clients, link) - if (w != c && w->isfullscreen && VISIBLEON(w, m)) + if (w != c && w->isfullscreen && m == w->mon && (w->tags & c->tags)) setfullscreen(w, 0); } -- cgit v1.2.3 From dd9d8d543cc511df44035c0e3fe32a5f17a31c29 Mon Sep 17 00:00:00 2001 From: Leonardo Hernández Hernández Date: Sun, 18 Dec 2022 11:39:50 -0600 Subject: remove selclient() selclient() does not work well when dealing newly mapped clients (specifically those mapped on invisible tags). This fixes various bugs related to things not working because selclient() would return NULL. References: 94c8bd604870365bc201524b63158623e6f32a8f --- dwl.c | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/dwl.c b/dwl.c index 4c045c2..f189139 100644 --- a/dwl.c +++ b/dwl.c @@ -280,7 +280,6 @@ static void rendermon(struct wl_listener *listener, void *data); static void requeststartdrag(struct wl_listener *listener, void *data); static void resize(Client *c, struct wlr_box geo, int interact); static void run(char *startup_cmd); -static Client *selclient(void); static void setcursor(struct wl_listener *listener, void *data); static void setfloating(Client *c, int floating); static void setfullscreen(Client *c, int fullscreen); @@ -1055,7 +1054,7 @@ destroydragicon(struct wl_listener *listener, void *data) struct wlr_drag_icon *icon = data; wlr_scene_node_destroy(icon->data); /* Focus enter isn't sent during drag, so refocus the focused node. */ - focusclient(selclient(), 1); + focusclient(focustop(selmon), 1); motionnotify(0); } @@ -1417,7 +1416,7 @@ keypressmod(struct wl_listener *listener, void *data) void killclient(const Arg *arg) { - Client *sel = selclient(); + Client *sel = focustop(selmon); if (sel) client_send_close(sel); } @@ -1940,15 +1939,6 @@ run(char *startup_cmd) wl_display_run(dpy); } -Client * -selclient(void) -{ - Client *c = wl_container_of(fstack.next, c, flink); - if (wl_list_empty(&fstack) || !VISIBLEON(c, selmon)) - return NULL; - return c; -} - void setcursor(struct wl_listener *listener, void *data) { @@ -2292,7 +2282,7 @@ startdrag(struct wl_listener *listener, void *data) void tag(const Arg *arg) { - Client *sel = selclient(); + Client *sel = focustop(selmon); if (sel && arg->ui & TAGMASK) { sel->tags = arg->ui & TAGMASK; focusclient(focustop(selmon), 1); @@ -2304,7 +2294,7 @@ tag(const Arg *arg) void tagmon(const Arg *arg) { - Client *sel = selclient(); + Client *sel = focustop(selmon); if (sel) setmon(sel, dirtomon(arg->i), 0); } @@ -2345,7 +2335,7 @@ tile(Monitor *m) void togglefloating(const Arg *arg) { - Client *sel = selclient(); + Client *sel = focustop(selmon); /* return if fullscreen */ if (sel && !sel->isfullscreen) setfloating(sel, !sel->isfloating); @@ -2354,7 +2344,7 @@ togglefloating(const Arg *arg) void togglefullscreen(const Arg *arg) { - Client *sel = selclient(); + Client *sel = focustop(selmon); if (sel) setfullscreen(sel, !sel->isfullscreen); } @@ -2363,7 +2353,7 @@ void toggletag(const Arg *arg) { unsigned int newtags; - Client *sel = selclient(); + Client *sel = focustop(selmon); if (!sel) return; newtags = sel->tags ^ (arg->ui & TAGMASK); @@ -2409,7 +2399,7 @@ unmaplayersurfacenotify(struct wl_listener *listener, void *data) arrangelayers(layersurface->mon); if (layersurface->layer_surface->surface == seat->keyboard_state.focused_surface) - focusclient(selclient(), 1); + focusclient(focustop(selmon), 1); motionnotify(0); } @@ -2430,7 +2420,7 @@ unmapnotify(struct wl_listener *listener, void *data) if (c == exclusive_focus) exclusive_focus = NULL; if (client_surface(c) == seat->keyboard_state.focused_surface) - focusclient(selclient(), 1); + focusclient(focustop(selmon), 1); } else { wl_list_remove(&c->link); setmon(c, NULL, 0); @@ -2535,7 +2525,7 @@ urgent(struct wl_listener *listener, void *data) struct wlr_xdg_activation_v1_request_activate_event *event = data; Client *c = NULL; int type = toplevel_from_wlr_surface(event->surface, &c, NULL); - if (type >= 0 && type != LayerShell && c != selclient()) { + if (type >= 0 && type != LayerShell && c != focustop(selmon)) { c->isurgent = 1; printstatus(); } @@ -2605,7 +2595,7 @@ xytonode(double x, double y, struct wlr_surface **psurface, void zoom(const Arg *arg) { - Client *c, *sel = selclient(); + Client *c, *sel = focustop(selmon); if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange || sel->isfloating) return; @@ -2699,7 +2689,7 @@ void sethints(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, set_hints); - if (c != selclient()) { + if (c != focustop(selmon)) { c->isurgent = xcb_icccm_wm_hints_get_urgency(c->surface.xwayland->hints); printstatus(); } -- cgit v1.2.3 From afacc9b0b50fe9c63700e62f7d2083c6edb9d17b Mon Sep 17 00:00:00 2001 From: Leonardo Hernández Hernández Date: Mon, 19 Dec 2022 21:43:15 -0600 Subject: force SSD when creating an xdg_toplevel_decoration object Closes: https://github.com/djpohly/dwl/issues/366 Not sure why GLFW apps do not get mapped when we do not tell them the decoration method --- dwl.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index f189139..2b4aa64 100644 --- a/dwl.c +++ b/dwl.c @@ -231,6 +231,7 @@ static void cleanupmon(struct wl_listener *listener, void *data); static void closemon(Monitor *m); static void commitlayersurfacenotify(struct wl_listener *listener, void *data); static void commitnotify(struct wl_listener *listener, void *data); +static void createdecoration(struct wl_listener *listener, void *data); static void createidleinhibitor(struct wl_listener *listener, void *data); static void createkeyboard(struct wlr_keyboard *keyboard); static void createlayersurface(struct wl_listener *listener, void *data); @@ -327,6 +328,7 @@ static struct wlr_compositor *compositor; static struct wlr_xdg_shell *xdg_shell; static struct wlr_xdg_activation_v1 *activation; +static struct wlr_xdg_decoration_manager_v1 *xdg_decoration_mgr; static struct wl_list clients; /* tiling order */ static struct wl_list fstack; /* focus order */ static struct wlr_idle *idle; @@ -369,6 +371,7 @@ static struct wl_listener new_input = {.notify = inputdevice}; static struct wl_listener new_virtual_keyboard = {.notify = virtualkeyboard}; static struct wl_listener new_output = {.notify = createmon}; static struct wl_listener new_xdg_surface = {.notify = createnotify}; +static struct wl_listener new_xdg_decoration = {.notify = createdecoration}; static struct wl_listener new_layer_shell_surface = {.notify = createlayersurface}; static struct wl_listener output_mgr_apply = {.notify = outputmgrapply}; static struct wl_listener output_mgr_test = {.notify = outputmgrtest}; @@ -770,6 +773,13 @@ commitnotify(struct wl_listener *listener, void *data) c->resize = 0; } +void +createdecoration(struct wl_listener *listener, void *data) +{ + struct wlr_xdg_toplevel_decoration_v1 *dec = data; + wlr_xdg_toplevel_decoration_v1_set_mode(dec, WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); +} + void createidleinhibitor(struct wl_listener *listener, void *data) { @@ -2181,7 +2191,8 @@ setup(void) wlr_server_decoration_manager_set_default_mode( wlr_server_decoration_manager_create(dpy), WLR_SERVER_DECORATION_MANAGER_MODE_SERVER); - wlr_xdg_decoration_manager_v1_create(dpy); + xdg_decoration_mgr = wlr_xdg_decoration_manager_v1_create(dpy); + wl_signal_add(&xdg_decoration_mgr->events.new_toplevel_decoration, &new_xdg_decoration); /* * Creates a cursor, which is a wlroots utility for tracking the cursor -- cgit v1.2.3 From 66828780094280064f5d3c0cb2982d28b0557e9e Mon Sep 17 00:00:00 2001 From: Leonardo Hernández Hernández Date: Mon, 12 Dec 2022 10:51:03 -0600 Subject: call arrange() or resize() depending on c->isfloating in commitnotify --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 2b4aa64..4fb8f49 100644 --- a/dwl.c +++ b/dwl.c @@ -764,7 +764,7 @@ commitnotify(struct wl_listener *listener, void *data) if (c->mon && !wlr_box_empty(&box) && (box.width != c->geom.width - 2 * c->bw || box.height != c->geom.height - 2 * c->bw)) - arrange(c->mon); + c->isfloating ? resize(c, c->geom, 1) : arrange(c->mon); /* mark a pending resize as completed */ if (c->resize && (c->resize <= c->surface.xdg->current.configure_serial -- cgit v1.2.3 From 92e7752203eb7812f2d333f8c6af1963fa743c09 Mon Sep 17 00:00:00 2001 From: Leonardo Hernández Hernández Date: Wed, 7 Dec 2022 16:26:37 -0600 Subject: disable client's scene node after create it (only XDGShell) will be enabled when setting up its monitor and arrange it Bug: https://github.com/djpohly/dwl/issues/306 --- dwl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dwl.c b/dwl.c index 4fb8f49..196c34d 100644 --- a/dwl.c +++ b/dwl.c @@ -1474,6 +1474,7 @@ mapnotify(struct wl_listener *listener, void *data) /* Create scene tree for this client and its border */ c->scene = wlr_scene_tree_create(layers[LyrTile]); + wlr_scene_node_set_enabled(&c->scene->node, c->type != XDGShell); c->scene_surface = c->type == XDGShell ? wlr_scene_xdg_surface_create(c->scene, c->surface.xdg) : wlr_scene_subsurface_tree_create(c->scene, client_surface(c)); -- cgit v1.2.3 From 7eaa01ac1f074511ae1013326172d51c6fdf8866 Mon Sep 17 00:00:00 2001 From: Leonardo Hernández Hernández Date: Tue, 6 Dec 2022 14:47:55 -0600 Subject: Revert "Revert "fix flickering when resizing/spawning windows"" This reverts commit 4a32293548667e68cd9a103e22368b8db1754deb. --- client.h | 15 +++++++++++++++ dwl.c | 29 ++++++----------------------- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/client.h b/client.h index 06c2073..064e093 100644 --- a/client.h +++ b/client.h @@ -240,6 +240,21 @@ client_is_mapped(Client *c) return c->surface.xdg->mapped; } +static inline int +client_is_rendered_on_mon(Client *c, Monitor *m) +{ + /* This is needed for when you don't want to check formal assignment, + * but rather actual displaying of the pixels. + * Usually VISIBLEON suffices and is also faster. */ + struct wlr_surface_output *s; + if (!c->scene->node.enabled) + return 0; + wl_list_for_each(s, &client_surface(c)->current_outputs, link) + if (s->output == m->wlr_output) + return 1; + return 0; +} + static inline int client_is_unmanaged(Client *c) { diff --git a/dwl.c b/dwl.c index 196c34d..8a21342 100644 --- a/dwl.c +++ b/dwl.c @@ -185,7 +185,6 @@ struct Monitor { unsigned int tagset[2]; double mfact; int nmaster; - int un_map; /* If a map/unmap happened on this monitor, then this should be true */ }; typedef struct { @@ -1531,8 +1530,6 @@ mapnotify(struct wl_listener *listener, void *data) } printstatus(); - c->mon->un_map = 1; - unset_fullscreen: m = c->mon ? c->mon : xytomon(c->geom.x, c->geom.y); wl_list_for_each(w, &clients, link) @@ -1834,30 +1831,19 @@ rendermon(struct wl_listener *listener, void *data) * generally at the output's refresh rate (e.g. 60Hz). */ Monitor *m = wl_container_of(listener, m, frame); Client *c; - int skip = 0; struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - /* Render if no XDG clients have an outstanding resize and are visible on * this monitor. */ - /* Checking m->un_map for every client is not optimal but works */ - wl_list_for_each(c, &clients, link) { - if ((c->resize && m->un_map) || (c->type == XDGShell - && (c->surface.xdg->pending.geometry.width != - c->surface.xdg->current.geometry.width - || c->surface.xdg->pending.geometry.height != - c->surface.xdg->current.geometry.height))) { - /* Lie */ - wlr_surface_send_frame_done(client_surface(c), &now); - skip = 1; - } - } - if (!skip && !wlr_scene_output_commit(m->scene_output)) + wl_list_for_each(c, &clients, link) + if (client_is_rendered_on_mon(c, m) && (!c->isfloating && c->resize)) + goto skip; + if (!wlr_scene_output_commit(m->scene_output)) return; +skip: /* Let clients know a frame has been rendered */ + clock_gettime(CLOCK_MONOTONIC, &now); wlr_scene_output_send_frame_done(m->scene_output, &now); - m->un_map = 0; } void @@ -2425,9 +2411,6 @@ unmapnotify(struct wl_listener *listener, void *data) grabc = NULL; } - if (c->mon) - c->mon->un_map = 1; - if (client_is_unmanaged(c)) { if (c == exclusive_focus) exclusive_focus = NULL; -- cgit v1.2.3 From 6ca011430a18980f12f7314cdeeff36051268a67 Mon Sep 17 00:00:00 2001 From: Leonardo Hernández Hernández Date: Wed, 21 Dec 2022 14:28:27 -0600 Subject: do not skip frames if a client is stopped and have a pending resize --- client.h | 26 ++++++++++++++++++++++++++ dwl.c | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/client.h b/client.h index 064e093..295e8ff 100644 --- a/client.h +++ b/client.h @@ -255,6 +255,32 @@ client_is_rendered_on_mon(Client *c, Monitor *m) return 0; } +static inline int +client_is_stopped(Client *c) +{ + int pid; + siginfo_t in = {0}; +#ifdef XWAYLAND + if (client_is_x11(c)) + return 0; +#endif + + wl_client_get_credentials(c->surface.xdg->client->client, &pid, NULL, NULL); + if (waitid(P_PID, pid, &in, WNOHANG|WCONTINUED|WSTOPPED|WNOWAIT) < 0) { + /* This process is not our child process, while is very unluckely that + * it is stopped, in order to do not skip frames assume that it is. */ + if (errno == ECHILD) + return 1; + } else if (in.si_pid) { + if (in.si_code == CLD_STOPPED || in.si_code == CLD_TRAPPED) + return 1; + if (in.si_code == CLD_CONTINUED) + return 0; + } + + return 0; +} + static inline int client_is_unmanaged(Client *c) { diff --git a/dwl.c b/dwl.c index 8a21342..43603db 100644 --- a/dwl.c +++ b/dwl.c @@ -1836,7 +1836,7 @@ rendermon(struct wl_listener *listener, void *data) /* Render if no XDG clients have an outstanding resize and are visible on * this monitor. */ wl_list_for_each(c, &clients, link) - if (client_is_rendered_on_mon(c, m) && (!c->isfloating && c->resize)) + if (client_is_rendered_on_mon(c, m) && (!c->isfloating && c->resize) && !client_is_stopped(c)) goto skip; if (!wlr_scene_output_commit(m->scene_output)) return; -- cgit v1.2.3 From be854cab35bc090ab3f6fbad3a0f93013294226d Mon Sep 17 00:00:00 2001 From: Leonardo Hernández Hernández Date: Wed, 14 Dec 2022 23:21:58 -0600 Subject: do not try to resize if size wouldn't change --- client.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client.h b/client.h index 295e8ff..5a45edc 100644 --- a/client.h +++ b/client.h @@ -345,6 +345,9 @@ client_set_size(Client *c, uint32_t width, uint32_t height) return 0; } #endif + if (width == c->surface.xdg->toplevel->current.width + && height ==c->surface.xdg->toplevel->current.height) + return 0; return wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, width, height); } -- cgit v1.2.3 From 05f4e23c43aa0d6e8fb8673a3b0d71653dc6dbbd Mon Sep 17 00:00:00 2001 From: Leonardo Hernández Hernández Date: Sat, 24 Dec 2022 17:54:19 -0600 Subject: bump version to 0.4-rc3 --- config.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.mk b/config.mk index 86b90ab..01aad14 100644 --- a/config.mk +++ b/config.mk @@ -1,4 +1,4 @@ -_VERSION = 0.4-rc2 +_VERSION = 0.4-rc3 VERSION = `git describe --long --tags --dirty 2>/dev/null || echo $(_VERSION)` PKG_CONFIG = pkg-config -- cgit v1.2.3