diff --git a/patches/bar/README.md b/patches/bar/README.md new file mode 100644 index 0000000..565fdbb --- /dev/null +++ b/patches/bar/README.md @@ -0,0 +1,31 @@ +### Description + +Add a bar identical to dwm's bar. + +To use a status-bar, you can pass in status text via stdin: +``` +slstatus -s | dwl +``` + +### Dependencies +* tllist (build dependency, required & pulled automatically by fcft) +* fcft +* pixman + +### Download +- [git branch](https://codeberg.org/sewn/dwl/src/branch/bar) +- [2024-06-27](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/bar/bar.patch) + +Below is a preview of the patch. + +![bar patch preview](bar.png) + +For colors, they are in `RRRR, GGGG, BBBB, AAAA` format due to inheriting the `pixman_color_t` type, an example for the color `0x282a36` would be `0x2828, 0x2a2a, 0x3636, 0xffff` + +### Authors +- [sewn](https://codeberg.org/sewn) + +### Credits +- [MadcowOG](https://github.com/MadcowOG) +- [kolumni](https://github.com/kolunmi/dwlb) + diff --git a/patches/bar/bar.patch b/patches/bar/bar.patch new file mode 100644 index 0000000..e5bdfdd --- /dev/null +++ b/patches/bar/bar.patch @@ -0,0 +1,1201 @@ +From 45e6f0f6d45342f41344ae035990306785175bed Mon Sep 17 00:00:00 2001 +From: sewn +Date: Wed, 26 Jun 2024 23:07:14 +0300 +Subject: [PATCH] Implement dwm bar clone + +--- + LICENSE | 3 +- + LICENSE.drwl | 21 +++ + Makefile | 2 +- + config.def.h | 31 ++-- + drwl.h | 284 +++++++++++++++++++++++++++++++++++++ + dwl.c | 391 ++++++++++++++++++++++++++++++++++++++++----------- + 6 files changed, 641 insertions(+), 91 deletions(-) + create mode 100644 LICENSE.drwl + create mode 100644 drwl.h + +diff --git a/LICENSE b/LICENSE +index 658085a..2c269b2 100644 +--- a/LICENSE ++++ b/LICENSE +@@ -2,7 +2,8 @@ dwl - dwm for Wayland + + Copyright © 2020 dwl team + +-See also the files LICENSE.tinywl, LICENSE.dwm and LICENSE.sway. ++See also the files LICENSE.tinywl, LICENSE.dwm, LICENSE.sway, and ++LICENSE.drwl. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by +diff --git a/LICENSE.drwl b/LICENSE.drwl +new file mode 100644 +index 0000000..4754b85 +--- /dev/null ++++ b/LICENSE.drwl +@@ -0,0 +1,21 @@ ++Copyright (c) 2023-2024 sewn ++Copyright (c) 2015 Eric Pruitt ++ ++Permission is hereby granted, free of charge, to any person obtaining ++a copy of this software and associated documentation files (the ++"Software"), to deal in the Software without restriction, including ++without limitation the rights to use, copy, modify, merge, publish, ++distribute, sublicense, and/or sell copies of the Software, and to ++permit persons to whom the Software is furnished to do so, subject to ++the following conditions: ++ ++The above copyright notice and this permission notice shall be ++included in all copies or substantial portions of the Software. ++ ++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ++LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION ++OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION ++WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +diff --git a/Makefile b/Makefile +index 9308656..eb5c9d8 100644 +--- a/Makefile ++++ b/Makefile +@@ -12,7 +12,7 @@ DWLDEVCFLAGS = -g -pedantic -Wall -Wextra -Wdeclaration-after-statement \ + -Wfloat-conversion + + # CFLAGS / LDFLAGS +-PKGS = wlroots wayland-server xkbcommon libinput $(XLIBS) ++PKGS = wlroots wayland-server xkbcommon libinput pixman-1 fcft $(XLIBS) + DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS) + LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(LIBS) + +diff --git a/config.def.h b/config.def.h +index 646a3d6..6024b7e 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -7,15 +7,21 @@ + static const int sloppyfocus = 1; /* focus follows mouse */ + static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */ + static const unsigned int borderpx = 1; /* border pixel of windows */ +-static const float rootcolor[] = COLOR(0x222222ff); +-static const float bordercolor[] = COLOR(0x444444ff); +-static const float focuscolor[] = COLOR(0x005577ff); +-static const float urgentcolor[] = COLOR(0xff0000ff); ++static const int showbar = 1; /* 0 means no bar */ ++static const int topbar = 1; /* 0 means bottom bar */ ++static const char *fonts[] = {"monospace:size=10"}; ++static const float rootcolor[] = COLOR(0x000000ff); + /* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */ + static const float fullscreen_bg[] = {0.1f, 0.1f, 0.1f, 1.0f}; /* You can also use glsl colors */ ++static uint32_t colors[][3] = { ++ /* fg bg border */ ++ [SchemeNorm] = { 0xbbbbbbff, 0x222222ff, 0x444444ff }, ++ [SchemeSel] = { 0xeeeeeeff, 0x005577ff, 0x005577ff }, ++ [SchemeUrg] = { 0, 0, 0x770000ff }, ++}; + + /* tagging - TAGCOUNT must be no greater than 31 */ +-#define TAGCOUNT (9) ++static char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; + + /* logging */ + static int log_level = WLR_ERROR; +@@ -126,6 +132,7 @@ static const Key keys[] = { + /* modifier key function argument */ + { MODKEY, XKB_KEY_p, spawn, {.v = menucmd} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} }, ++ { MODKEY, XKB_KEY_b, togglebar, {0} }, + { MODKEY, XKB_KEY_j, focusstack, {.i = +1} }, + { MODKEY, XKB_KEY_k, focusstack, {.i = -1} }, + { MODKEY, XKB_KEY_i, incnmaster, {.i = +1} }, +@@ -169,7 +176,15 @@ static const Key keys[] = { + }; + + static const Button buttons[] = { +- { MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} }, +- { MODKEY, BTN_MIDDLE, togglefloating, {0} }, +- { MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} }, ++ { ClkLtSymbol, 0, BTN_LEFT, setlayout, {.v = &layouts[0]} }, ++ { ClkLtSymbol, 0, BTN_RIGHT, setlayout, {.v = &layouts[2]} }, ++ { ClkTitle, 0, BTN_MIDDLE, zoom, {0} }, ++ { ClkStatus, 0, BTN_MIDDLE, spawn, {.v = termcmd} }, ++ { ClkClient, MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} }, ++ { ClkClient, MODKEY, BTN_MIDDLE, togglefloating, {0} }, ++ { ClkClient, MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} }, ++ { ClkTagBar, 0, BTN_LEFT, view, {0} }, ++ { ClkTagBar, 0, BTN_RIGHT, toggleview, {0} }, ++ { ClkTagBar, MODKEY, BTN_LEFT, tag, {0} }, ++ { ClkTagBar, MODKEY, BTN_RIGHT, toggletag, {0} }, + }; +diff --git a/drwl.h b/drwl.h +new file mode 100644 +index 0000000..afcb2bd +--- /dev/null ++++ b/drwl.h +@@ -0,0 +1,284 @@ ++/* ++ * drwl - https://codeberg.org/sewn/drwl ++ * See LICENSE.drwl file for copyright and license details. ++ */ ++#pragma once ++ ++#include ++#include ++#include ++ ++#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) ++ ++enum { ColFg, ColBg, ColBorder }; /* colorscheme index */ ++ ++typedef struct { ++ pixman_image_t *pix; ++ struct fcft_font *font; ++ uint32_t *scheme; ++} Drwl; ++ ++#define UTF_INVALID 0xFFFD ++#define UTF_SIZ 4 ++ ++static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; ++static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; ++static const uint32_t utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; ++static const uint32_t utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; ++ ++static inline uint32_t ++utf8decodebyte(const char c, size_t *i) ++{ ++ for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) ++ if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) ++ return (unsigned char)c & ~utfmask[*i]; ++ return 0; ++} ++ ++static inline size_t ++utf8decode(const char *c, uint32_t *u) ++{ ++ size_t i, j, len, type; ++ uint32_t udecoded; ++ ++ *u = UTF_INVALID; ++ udecoded = utf8decodebyte(c[0], &len); ++ if (!BETWEEN(len, 1, UTF_SIZ)) ++ return 1; ++ for (i = 1, j = 1; i < UTF_SIZ && j < len; ++i, ++j) { ++ udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); ++ if (type) ++ return j; ++ } ++ if (j < len) ++ return 0; ++ *u = udecoded; ++ if (!BETWEEN(*u, utfmin[len], utfmax[len]) || BETWEEN(*u, 0xD800, 0xDFFF)) ++ *u = UTF_INVALID; ++ for (i = 1; *u > utfmax[i]; ++i) ++ ; ++ return len; ++} ++ ++static int ++drwl_init(void) ++{ ++ fcft_set_scaling_filter(FCFT_SCALING_FILTER_LANCZOS3); ++ return fcft_init(FCFT_LOG_COLORIZE_AUTO, 0, FCFT_LOG_CLASS_ERROR); ++} ++ ++static Drwl * ++drwl_create(void) ++{ ++ Drwl *drwl; ++ ++ if (!(drwl = calloc(1, sizeof(Drwl)))) ++ return NULL; ++ ++ return drwl; ++} ++ ++static void ++drwl_setfont(Drwl *drwl, struct fcft_font *font) ++{ ++ if (drwl) ++ drwl->font = font; ++} ++ ++static struct fcft_font * ++drwl_load_font(Drwl *drwl, size_t fontcount, ++ const char *fonts[static fontcount], const char *attributes) ++{ ++ struct fcft_font *font = fcft_from_name(fontcount, fonts, attributes); ++ if (drwl) ++ drwl_setfont(drwl, font); ++ return font; ++} ++ ++static void ++drwl_destroy_font(struct fcft_font *font) ++{ ++ fcft_destroy(font); ++} ++ ++static inline pixman_color_t ++convert_color(uint32_t clr) ++{ ++ return (pixman_color_t){ ++ ((clr >> 24) & 0xFF) * 0x101, ++ ((clr >> 16) & 0xFF) * 0x101, ++ ((clr >> 8) & 0xFF) * 0x101, ++ (clr & 0xFF) * 0x101 ++ }; ++} ++ ++static void ++drwl_setscheme(Drwl *drwl, uint32_t *scm) ++{ ++ if (drwl) ++ drwl->scheme = scm; ++} ++ ++static inline int ++drwl_stride(unsigned int width) ++{ ++ return (((PIXMAN_FORMAT_BPP(PIXMAN_a8r8g8b8) * width + 7) / 8 + 4 - 1) & -4); ++} ++ ++static void ++drwl_prepare_drawing(Drwl *drwl, unsigned int w, unsigned int h, ++ uint32_t *bits, int stride) ++{ ++ pixman_region32_t clip; ++ ++ if (!drwl) ++ return; ++ ++ drwl->pix = pixman_image_create_bits_no_clear( ++ PIXMAN_a8r8g8b8, w, h, bits, stride); ++ pixman_region32_init_rect(&clip, 0, 0, w, h); ++ pixman_image_set_clip_region32(drwl->pix, &clip); ++ pixman_region32_fini(&clip); ++} ++ ++static void ++drwl_rect(Drwl *drwl, ++ int x, int y, unsigned int w, unsigned int h, ++ int filled, int invert) ++{ ++ pixman_color_t clr; ++ if (!drwl || !drwl->scheme || !drwl->pix) ++ return; ++ ++ clr = convert_color(drwl->scheme[invert ? ColBg : ColFg]); ++ if (filled) ++ pixman_image_fill_rectangles(PIXMAN_OP_SRC, drwl->pix, &clr, 1, ++ &(pixman_rectangle16_t){x, y, w, h}); ++ else ++ pixman_image_fill_rectangles(PIXMAN_OP_SRC, drwl->pix, &clr, 4, ++ (pixman_rectangle16_t[4]){ ++ { x, y, w, 1 }, ++ { x, y + h - 1, w, 1 }, ++ { x, y, 1, h }, ++ { x + w - 1, y, 1, h }}); ++} ++ ++static int ++drwl_text(Drwl *drwl, ++ int x, int y, unsigned int w, unsigned int h, ++ unsigned int lpad, const char *text, int invert) ++{ ++ int ty; ++ int utf8charlen, render = x || y || w || h; ++ long x_kern; ++ uint32_t cp = 0, last_cp = 0; ++ pixman_color_t clr; ++ pixman_image_t *fg_pix = NULL; ++ int noellipsis = 0; ++ const struct fcft_glyph *glyph, *eg; ++ int fcft_subpixel_mode = FCFT_SUBPIXEL_DEFAULT; ++ ++ if (!drwl || (render && (!drwl->scheme || !w || !drwl->pix)) || !text || !drwl->font) ++ return 0; ++ ++ if (!render) { ++ w = invert ? invert : ~invert; ++ } else { ++ clr = convert_color(drwl->scheme[invert ? ColBg : ColFg]); ++ fg_pix = pixman_image_create_solid_fill(&clr); ++ ++ drwl_rect(drwl, x, y, w, h, 1, !invert); ++ ++ x += lpad; ++ w -= lpad; ++ } ++ ++ if (render && (drwl->scheme[ColBg] & 0xFF) != 0xFF) ++ fcft_subpixel_mode = FCFT_SUBPIXEL_NONE; ++ ++ // U+2026 == … ++ eg = fcft_rasterize_char_utf32(drwl->font, 0x2026, fcft_subpixel_mode); ++ ++ while (*text) { ++ utf8charlen = utf8decode(text, &cp); ++ ++ glyph = fcft_rasterize_char_utf32(drwl->font, cp, fcft_subpixel_mode); ++ if (!glyph) ++ continue; ++ ++ x_kern = 0; ++ if (last_cp) ++ fcft_kerning(drwl->font, last_cp, cp, &x_kern, NULL); ++ last_cp = cp; ++ ++ ty = y + (h - drwl->font->height) / 2 + drwl->font->ascent; ++ ++ /* draw ellipsis if remaining text doesn't fit */ ++ if (!noellipsis && x_kern + glyph->advance.x + eg->advance.x > w && *(text + 1) != '\0') { ++ if (drwl_text(drwl, 0, 0, 0, 0, 0, text, 0) ++ - glyph->advance.x < eg->advance.x) { ++ noellipsis = 1; ++ } else { ++ w -= eg->advance.x; ++ pixman_image_composite32( ++ PIXMAN_OP_OVER, fg_pix, eg->pix, drwl->pix, 0, 0, 0, 0, ++ x + eg->x, ty - eg->y, eg->width, eg->height); ++ } ++ } ++ ++ if ((x_kern + glyph->advance.x) > w) ++ break; ++ ++ x += x_kern; ++ ++ if (render && pixman_image_get_format(glyph->pix) == PIXMAN_a8r8g8b8) ++ // pre-rendered glyphs (eg. emoji) ++ pixman_image_composite32( ++ PIXMAN_OP_OVER, glyph->pix, NULL, drwl->pix, 0, 0, 0, 0, ++ x + glyph->x, ty - glyph->y, glyph->width, glyph->height); ++ else if (render) ++ pixman_image_composite32( ++ PIXMAN_OP_OVER, fg_pix, glyph->pix, drwl->pix, 0, 0, 0, 0, ++ x + glyph->x, ty - glyph->y, glyph->width, glyph->height); ++ ++ text += utf8charlen; ++ x += glyph->advance.x; ++ w -= glyph->advance.x; ++ } ++ ++ if (render) ++ pixman_image_unref(fg_pix); ++ ++ return x + (render ? w : 0); ++} ++ ++static unsigned int ++drwl_font_getwidth(Drwl *drwl, const char *text) ++{ ++ if (!drwl || !drwl->font || !text) ++ return 0; ++ return drwl_text(drwl, 0, 0, 0, 0, 0, text, 0); ++} ++ ++static void ++drwl_finish_drawing(Drwl *drwl) ++{ ++ if (drwl && drwl->pix) ++ pixman_image_unref(drwl->pix); ++} ++ ++static void ++drwl_destroy(Drwl *drwl) ++{ ++ if (drwl->pix) ++ pixman_image_unref(drwl->pix); ++ if (drwl->font) ++ drwl_destroy_font(drwl->font); ++ free(drwl); ++} ++ ++static void ++drwl_fini(void) ++{ ++ fcft_fini(); ++} +diff --git a/dwl.c b/dwl.c +index 9fb50a7..9fdddc7 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -4,6 +4,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -56,6 +57,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -66,6 +68,7 @@ + #endif + + #include "util.h" ++#include "drwl.h" + + /* macros */ + #define MAX(A, B) ((A) > (B) ? (A) : (B)) +@@ -76,14 +79,17 @@ + #define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags])) + #define LENGTH(X) (sizeof X / sizeof X[0]) + #define END(A) ((A) + LENGTH(A)) +-#define TAGMASK ((1u << TAGCOUNT) - 1) ++#define TAGMASK ((1u << LENGTH(tags)) - 1) + #define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L))) + #define LISTEN_STATIC(E, H) do { static struct wl_listener _l = {.notify = (H)}; wl_signal_add((E), &_l); } while (0) ++#define TEXTW(mon, text) (drwl_font_getwidth(mon->drw, text) + mon->lrpad) + + /* enums */ ++enum { SchemeNorm, SchemeSel, SchemeUrg }; /* color schemes */ + enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */ + enum { XDGShell, LayerShell, X11 }; /* client types */ + enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrTop, LyrFS, LyrOverlay, LyrBlock, NUM_LAYERS }; /* scene layers */ ++enum { ClkTagBar, ClkLtSymbol, ClkStatus, ClkTitle, ClkClient, ClkRoot }; /* clicks */ + #ifdef XWAYLAND + enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar, + NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */ +@@ -97,6 +103,7 @@ typedef union { + } Arg; + + typedef struct { ++ unsigned int click; + unsigned int mod; + unsigned int button; + void (*func)(const Arg *); +@@ -191,6 +198,7 @@ struct Monitor { + struct wl_list link; + struct wlr_output *wlr_output; + struct wlr_scene_output *scene_output; ++ struct wlr_scene_buffer *scene_buffer; /* bar buffer */ + struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */ + struct wl_listener frame; + struct wl_listener destroy; +@@ -198,6 +206,11 @@ struct Monitor { + struct wl_listener destroy_lock_surface; + struct wlr_session_lock_surface_v1 *lock_surface; + struct wlr_box m; /* monitor area, layout-relative */ ++ struct { ++ int width, height; ++ int real_width, real_height; /* non-scaled */ ++ float scale; ++ } b; /* bar area */ + struct wlr_box w; /* window area, layout-relative */ + struct wl_list layers[4]; /* LayerSurface.link */ + const Layout *lt[2]; +@@ -207,10 +220,19 @@ struct Monitor { + float mfact; + int gamma_lut_changed; + int nmaster; ++ int showbar; + char ltsymbol[16]; + int asleep; ++ Drwl *drw; ++ int lrpad; + }; + ++typedef struct { ++ struct wlr_buffer base; ++ size_t stride; ++ uint32_t data[]; ++} Buffer; ++ + typedef struct { + const char *name; + float mfact; +@@ -251,6 +273,10 @@ static void arrangelayer(Monitor *m, struct wl_list *list, + struct wlr_box *usable_area, int exclusive); + static void arrangelayers(Monitor *m); + static void axisnotify(struct wl_listener *listener, void *data); ++static bool bar_accepts_input(struct wlr_scene_buffer *buffer, double *sx, double *sy); ++static void buffer_destroy(struct wlr_buffer *buffer); ++static bool buffer_begin_data_ptr_access(struct wlr_buffer *buffer, uint32_t flags, void **data, uint32_t *format, size_t *stride); ++static void buffer_end_data_ptr_access(struct wlr_buffer *buffer); + static void buttonpress(struct wl_listener *listener, void *data); + static void chvt(const Arg *arg); + static void checkidleinhibitor(struct wlr_surface *exclude); +@@ -284,6 +310,8 @@ static void destroysessionlock(struct wl_listener *listener, void *data); + static void destroysessionmgr(struct wl_listener *listener, void *data); + static void destroykeyboardgroup(struct wl_listener *listener, void *data); + static Monitor *dirtomon(enum wlr_direction dir); ++static void drawbar(Monitor *m); ++static void drawbars(void); + static void focusclient(Client *c, int lift); + static void focusmon(const Arg *arg); + static void focusstack(const Arg *arg); +@@ -312,7 +340,6 @@ static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int + static void outputmgrtest(struct wl_listener *listener, void *data); + static void pointerfocus(Client *c, struct wlr_surface *surface, + double sx, double sy, uint32_t time); +-static void printstatus(void); + static void powermgrsetmode(struct wl_listener *listener, void *data); + static void quit(const Arg *arg); + static void rendermon(struct wl_listener *listener, void *data); +@@ -334,9 +361,11 @@ static void setsel(struct wl_listener *listener, void *data); + static void setup(void); + static void spawn(const Arg *arg); + static void startdrag(struct wl_listener *listener, void *data); ++static int status_in(int fd, unsigned int mask, void *data); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *m); ++static void togglebar(const Arg *arg); + static void togglefloating(const Arg *arg); + static void togglefullscreen(const Arg *arg); + static void toggletag(const Arg *arg); +@@ -345,6 +374,7 @@ static void unlocksession(struct wl_listener *listener, void *data); + static void unmaplayersurfacenotify(struct wl_listener *listener, void *data); + static void unmapnotify(struct wl_listener *listener, void *data); + static void updatemons(struct wl_listener *listener, void *data); ++static void updatebar(Monitor *m); + static void updatetitle(struct wl_listener *listener, void *data); + static void urgent(struct wl_listener *listener, void *data); + static void view(const Arg *arg); +@@ -411,6 +441,15 @@ static struct wlr_box sgeom; + static struct wl_list mons; + static Monitor *selmon; + ++static char stext[256]; ++static struct wl_event_source *status_event_source; ++ ++static const struct wlr_buffer_impl buffer_impl = { ++ .destroy = buffer_destroy, ++ .begin_data_ptr_access = buffer_begin_data_ptr_access, ++ .end_data_ptr_access = buffer_end_data_ptr_access ++}; ++ + #ifdef XWAYLAND + static void activatex11(struct wl_listener *listener, void *data); + static void associatex11(struct wl_listener *listener, void *data); +@@ -551,6 +590,11 @@ arrangelayers(Monitor *m) + if (!m->wlr_output->enabled) + return; + ++ if (m->showbar) { ++ usable_area.height -= m->b.real_height; ++ usable_area.y += topbar ? m->b.real_height : 0; ++ } ++ + /* Arrange exclusive surfaces from top->bottom */ + for (i = 3; i >= 0; i--) + arrangelayer(m, &m->layers[i], &usable_area, 1); +@@ -593,17 +637,82 @@ axisnotify(struct wl_listener *listener, void *data) + event->delta_discrete, event->source); + } + ++bool ++bar_accepts_input(struct wlr_scene_buffer *buffer, double *sx, double *sy) ++{ ++ return true; ++} ++ ++void ++buffer_destroy(struct wlr_buffer *wlr_buffer) ++{ ++ Buffer *buf; ++ buf = wl_container_of(wlr_buffer, buf, base); ++ free(buf); ++} ++ ++bool ++buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, uint32_t flags, ++ void **data, uint32_t *format, size_t *stride) ++{ ++ Buffer *buf; ++ buf = wl_container_of(wlr_buffer, buf, base); ++ ++ if (flags & WLR_BUFFER_DATA_PTR_ACCESS_WRITE) return false; ++ ++ *data = buf->data; ++ *stride = buf->stride; ++ *format = DRM_FORMAT_ARGB8888; ++ ++ return true; ++} ++ ++void ++buffer_end_data_ptr_access(struct wlr_buffer *buffer) ++{ ++} ++ + void + buttonpress(struct wl_listener *listener, void *data) + { ++ unsigned int i = 0, x = 0; ++ unsigned int click; + struct wlr_pointer_button_event *event = data; + struct wlr_keyboard *keyboard; ++ struct wlr_scene_node *node; ++ struct wlr_scene_buffer *buffer; + uint32_t mods; ++ Arg arg = {0}; + Client *c; + const Button *b; + + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); + ++ click = ClkRoot; ++ xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL); ++ if (c) ++ click = ClkClient; ++ ++ if (!c && !exclusive_focus && ++ (node = wlr_scene_node_at(&layers[LyrBottom]->node, cursor->x, cursor->y, NULL, NULL)) && ++ (buffer = wlr_scene_buffer_from_node(node)) && buffer == selmon->scene_buffer) { ++ cursor->x *= selmon->wlr_output->scale; ++ cursor->y *= selmon->wlr_output->scale; ++ x = selmon->m.x; ++ do ++ x += TEXTW(selmon, tags[i]); ++ while (cursor->x >= x && ++i < LENGTH(tags)); ++ if (i < LENGTH(tags)) { ++ click = ClkTagBar; ++ arg.ui = 1 << i; ++ } else if (cursor->x < x + TEXTW(selmon, selmon->ltsymbol)) ++ click = ClkLtSymbol; ++ else if (cursor->x > selmon->w.width - (int)TEXTW(selmon, stext)) ++ click = ClkStatus; ++ else ++ click = ClkTitle; ++ } ++ + switch (event->state) { + case WLR_BUTTON_PRESSED: + cursor_mode = CurPressed; +@@ -612,15 +721,14 @@ buttonpress(struct wl_listener *listener, void *data) + + /* Change focus if the button was _pressed_ over a client */ + xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL); +- if (c && (!client_is_unmanaged(c) || client_wants_focus(c))) ++ if (click == ClkClient && (!client_is_unmanaged(c) || client_wants_focus(c))) + focusclient(c, 1); + + keyboard = wlr_seat_get_keyboard(seat); + mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; + for (b = buttons; b < END(buttons); b++) { +- if (CLEANMASK(mods) == CLEANMASK(b->mod) && +- event->button == b->button && b->func) { +- b->func(&b->arg); ++ if (CLEANMASK(mods) == CLEANMASK(b->mod) && event->button == b->button && click == b->click && b->func) { ++ b->func(click == ClkTagBar && b->arg.i == 0 ? &arg : &b->arg); + return; + } + } +@@ -691,6 +799,8 @@ cleanup(void) + /* Destroy after the wayland display (when the monitors are already destroyed) + to avoid destroying them with an invalid scene output. */ + wlr_scene_node_destroy(&scene->tree.node); ++ ++ drwl_fini(); + } + + void +@@ -706,6 +816,8 @@ cleanupmon(struct wl_listener *listener, void *data) + wlr_layer_surface_v1_destroy(l->layer_surface); + } + ++ drwl_destroy(m->drw); ++ + wl_list_remove(&m->destroy.link); + wl_list_remove(&m->frame.link); + wl_list_remove(&m->link); +@@ -745,7 +857,7 @@ closemon(Monitor *m) + setmon(c, selmon, c->tags); + } + focusclient(focustop(selmon), 1); +- printstatus(); ++ drawbars(); + } + + void +@@ -970,8 +1082,16 @@ createmon(struct wl_listener *listener, void *data) + wlr_output_commit_state(wlr_output, &state); + wlr_output_state_finish(&state); + ++ if (!(m->drw = drwl_create())) ++ die("failed to create drwl context"); ++ ++ m->scene_buffer = wlr_scene_buffer_create(layers[LyrBottom], NULL); ++ m->scene_buffer->point_accepts_input = bar_accepts_input; ++ m->showbar = showbar; ++ updatebar(m); ++ + wl_list_insert(&mons, &m->link); +- printstatus(); ++ drawbars(); + + /* The xdg-protocol specifies: + * +@@ -1303,6 +1423,89 @@ dirtomon(enum wlr_direction dir) + return selmon; + } + ++void ++drawbar(Monitor *m) ++{ ++ int x, w, tw = 0; ++ int boxs = m->drw->font->height / 9; ++ int boxw = m->drw->font->height / 6 + 2; ++ uint32_t i, occ = 0, urg = 0; ++ int32_t stride, size; ++ Client *c; ++ Buffer *buf; ++ ++ if (!m->showbar) ++ return; ++ ++ stride = drwl_stride(m->b.width); ++ size = stride * m->b.height; ++ ++ buf = ecalloc(1, sizeof(Buffer) + size); ++ buf->stride = stride; ++ wlr_buffer_init(&buf->base, &buffer_impl, m->b.width, m->b.height); ++ ++ drwl_prepare_drawing(m->drw, m->b.width, m->b.height, buf->data, stride); ++ ++ /* draw status first so it can be overdrawn by tags later */ ++ if (m == selmon) { /* status is only drawn on selected monitor */ ++ drwl_setscheme(m->drw, colors[SchemeNorm]); ++ tw = TEXTW(m, stext) - m->lrpad + 2; /* 2px right padding */ ++ drwl_text(m->drw, m->b.width - tw, 0, tw, m->b.height, 0, stext, 0); ++ } ++ ++ wl_list_for_each(c, &clients, link) { ++ if (c->mon != m) ++ continue; ++ occ |= c->tags; ++ if (c->isurgent) ++ urg |= c->tags; ++ } ++ x = 0; ++ c = focustop(m); ++ for (i = 0; i < LENGTH(tags); i++) { ++ w = TEXTW(m, tags[i]); ++ drwl_setscheme(m->drw, colors[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); ++ drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, tags[i], urg & 1 << i); ++ if (occ & 1 << i) ++ drwl_rect(m->drw, x + boxs, boxs, boxw, boxw, ++ m == selmon && c && c->tags & 1 << i, ++ urg & 1 << i); ++ x += w; ++ } ++ w = TEXTW(m, m->ltsymbol); ++ drwl_setscheme(m->drw, colors[SchemeNorm]); ++ x = drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, m->ltsymbol, 0); ++ ++ if ((w = m->b.width - tw - x) > m->b.height) { ++ if (c) { ++ drwl_setscheme(m->drw, colors[m == selmon ? SchemeSel : SchemeNorm]); ++ drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, client_get_title(c), 0); ++ if (c && c->isfloating) ++ drwl_rect(m->drw, x + boxs, boxs, boxw, boxw, 0, 0); ++ } else { ++ drwl_setscheme(m->drw, colors[SchemeNorm]); ++ drwl_rect(m->drw, x, 0, w, m->b.height, 1, 1); ++ } ++ } ++ ++ drwl_finish_drawing(m->drw); ++ wlr_scene_buffer_set_dest_size(m->scene_buffer, ++ m->b.real_width, m->b.real_height); ++ wlr_scene_node_set_position(&m->scene_buffer->node, m->m.x, ++ m->m.y + (topbar ? 0 : m->m.height - m->b.real_height)); ++ wlr_scene_buffer_set_buffer(m->scene_buffer, &buf->base); ++ wlr_buffer_drop(&buf->base); ++} ++ ++void ++drawbars(void) ++{ ++ Monitor *m = NULL; ++ ++ wl_list_for_each(m, &mons, link) ++ drawbar(m); ++} ++ + void + focusclient(Client *c, int lift) + { +@@ -1338,13 +1541,13 @@ focusclient(Client *c, int lift) + /* Don't change border color if there is an exclusive focus or we are + * handling a drag operation */ + if (!exclusive_focus && !seat->drag) +- client_set_border_color(c, focuscolor); ++ client_set_border_color(c, (float[])COLOR(colors[SchemeSel][ColBorder])); + } + + /* Deactivate old client if focus is changing */ + if (old && (!c || client_surface(c) != old)) { + /* If an overlay is focused, don't focus or activate the client, +- * but only update its position in fstack to render its border with focuscolor ++ * but only update its position in fstack to render its border with its color + * and focus it after the overlay is closed. */ + if (old_client_type == LayerShell && wlr_scene_node_coords( + &old_l->scene->node, &unused_lx, &unused_ly) +@@ -1355,12 +1558,11 @@ focusclient(Client *c, int lift) + /* Don't deactivate old client if the new one wants focus, as this causes issues with winecfg + * and probably other clients */ + } else if (old_c && !client_is_unmanaged(old_c) && (!c || !client_wants_focus(c))) { +- client_set_border_color(old_c, bordercolor); +- ++ client_set_border_color(old_c, (float[])COLOR(colors[SchemeNorm][ColBorder])); + client_activate_surface(old, 0); + } + } +- printstatus(); ++ drawbars(); + + if (!c) { + /* With no client, all we have left is to clear focus */ +@@ -1664,7 +1866,7 @@ mapnotify(struct wl_listener *listener, void *data) + + for (i = 0; i < 4; i++) { + c->border[i] = wlr_scene_rect_create(c->scene, 0, 0, +- c->isurgent ? urgentcolor : bordercolor); ++ (float[])COLOR(colors[c->isurgent ? SchemeUrg : SchemeNorm][ColBorder])); + c->border[i]->node.data = c; + } + +@@ -1687,7 +1889,7 @@ mapnotify(struct wl_listener *listener, void *data) + } else { + applyrules(c); + } +- printstatus(); ++ drawbars(); + + unset_fullscreen: + m = c->mon ? c->mon : xytomon(c->geom.x, c->geom.y); +@@ -1980,46 +2182,6 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, + wlr_seat_pointer_notify_motion(seat, time, sx, sy); + } + +-void +-printstatus(void) +-{ +- Monitor *m = NULL; +- Client *c; +- uint32_t occ, urg, sel; +- const char *appid, *title; +- +- wl_list_for_each(m, &mons, link) { +- occ = urg = 0; +- wl_list_for_each(c, &clients, link) { +- if (c->mon != m) +- continue; +- occ |= c->tags; +- if (c->isurgent) +- urg |= c->tags; +- } +- if ((c = focustop(m))) { +- title = client_get_title(c); +- appid = client_get_appid(c); +- printf("%s title %s\n", m->wlr_output->name, title ? title : broken); +- printf("%s appid %s\n", m->wlr_output->name, appid ? appid : broken); +- printf("%s fullscreen %d\n", m->wlr_output->name, c->isfullscreen); +- printf("%s floating %d\n", m->wlr_output->name, c->isfloating); +- sel = c->tags; +- } else { +- printf("%s title \n", m->wlr_output->name); +- printf("%s appid \n", m->wlr_output->name); +- printf("%s fullscreen \n", m->wlr_output->name); +- printf("%s floating \n", m->wlr_output->name); +- sel = 0; +- } +- +- printf("%s selmon %u\n", m->wlr_output->name, m == selmon); +- printf("%s tags %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32"\n", +- m->wlr_output->name, occ, m->tagset[m->seltags], sel, urg); +- printf("%s layout %s\n", m->wlr_output->name, m->ltsymbol); +- } +- fflush(stdout); +-} + + void + powermgrsetmode(struct wl_listener *listener, void *data) +@@ -2173,24 +2335,16 @@ run(char *startup_cmd) + + /* Now that the socket exists and the backend is started, run the startup command */ + if (startup_cmd) { +- int piperw[2]; +- if (pipe(piperw) < 0) +- die("startup: pipe:"); + if ((child_pid = fork()) < 0) + die("startup: fork:"); + if (child_pid == 0) { ++ close(STDIN_FILENO); + setsid(); +- dup2(piperw[0], STDIN_FILENO); +- close(piperw[0]); +- close(piperw[1]); + execl("/bin/sh", "/bin/sh", "-c", startup_cmd, NULL); + die("startup: execl:"); + } +- dup2(piperw[1], STDOUT_FILENO); +- close(piperw[1]); +- close(piperw[0]); + } +- printstatus(); ++ drawbars(); + + /* At this point the outputs are initialized, choose initial selmon based on + * cursor position, and set default cursor image */ +@@ -2256,7 +2410,7 @@ setfloating(Client *c, int floating) + (p && p->isfullscreen) ? LyrFS + : c->isfloating ? LyrFloat : LyrTile]); + arrange(c->mon); +- printstatus(); ++ drawbars(); + } + + void +@@ -2279,7 +2433,7 @@ setfullscreen(Client *c, int fullscreen) + resize(c, c->prev, 0); + } + arrange(c->mon); +- printstatus(); ++ drawbars(); + } + + void +@@ -2304,7 +2458,7 @@ setlayout(const Arg *arg) + selmon->lt[selmon->sellt] = (Layout *)arg->v; + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, LENGTH(selmon->ltsymbol)); + arrange(selmon); +- printstatus(); ++ drawbar(selmon); + } + + /* arg > 1.0 will set mfact absolutely */ +@@ -2377,6 +2531,7 @@ setup(void) + for (i = 0; i < (int)LENGTH(sig); i++) + sigaction(sig[i], &sa, NULL); + ++ + wlr_log_init(log_level, NULL); + + /* The Wayland display is managed by libwayland. It handles accepting +@@ -2563,6 +2718,11 @@ setup(void) + + wlr_scene_set_presentation(scene, wlr_presentation_create(dpy, backend)); + ++ drwl_init(); ++ ++ status_event_source = wl_event_loop_add_fd(wl_display_get_event_loop(dpy), ++ STDIN_FILENO, WL_EVENT_READABLE, status_in, NULL); ++ + /* Make sure XWayland clients don't connect to the parent X server, + * e.g when running in the x11 backend or the wayland backend and the + * compositor has Xwayland support */ +@@ -2587,6 +2747,7 @@ void + spawn(const Arg *arg) + { + if (fork() == 0) { ++ close(STDIN_FILENO); + dup2(STDERR_FILENO, STDOUT_FILENO); + setsid(); + execvp(((char **)arg->v)[0], (char **)arg->v); +@@ -2605,6 +2766,30 @@ startdrag(struct wl_listener *listener, void *data) + LISTEN_STATIC(&drag->icon->events.destroy, destroydragicon); + } + ++int ++status_in(int fd, unsigned int mask, void *data) ++{ ++ char status[1024]; ++ ssize_t n; ++ ++ if (mask & WL_EVENT_ERROR) ++ die("status in event error"); ++ if (mask & WL_EVENT_HANGUP) ++ wl_event_source_remove(status_event_source); ++ ++ n = read(fd, status, sizeof(status) - 1); ++ if (n < 0 && errno != EWOULDBLOCK) ++ die("read:"); ++ ++ status[n] = '\0'; ++ status[strcspn(status, "\n")] = '\0'; ++ ++ strncpy(stext, status, sizeof(stext)); ++ drawbars(); ++ ++ return 0; ++} ++ + void + tag(const Arg *arg) + { +@@ -2615,7 +2800,7 @@ tag(const Arg *arg) + sel->tags = arg->ui & TAGMASK; + focusclient(focustop(selmon), 1); + arrange(selmon); +- printstatus(); ++ drawbars(); + } + + void +@@ -2660,6 +2845,14 @@ tile(Monitor *m) + } + } + ++void ++togglebar(const Arg *arg) ++{ ++ selmon->showbar = !selmon->showbar; ++ wlr_scene_node_set_enabled(&selmon->scene_buffer->node, selmon->showbar); ++ arrangelayers(selmon); ++} ++ + void + togglefloating(const Arg *arg) + { +@@ -2688,7 +2881,7 @@ toggletag(const Arg *arg) + sel->tags = newtags; + focusclient(focustop(selmon), 1); + arrange(selmon); +- printstatus(); ++ drawbars(); + } + + void +@@ -2701,7 +2894,7 @@ toggleview(const Arg *arg) + selmon->tagset[selmon->seltags] = newtagset; + focusclient(focustop(selmon), 1); + arrange(selmon); +- printstatus(); ++ drawbars(); + } + + void +@@ -2749,7 +2942,7 @@ unmapnotify(struct wl_listener *listener, void *data) + } + + wlr_scene_node_destroy(&c->scene->node); +- printstatus(); ++ drawbars(); + motionnotify(0, NULL, 0, 0, 0, 0); + } + +@@ -2849,6 +3042,14 @@ updatemons(struct wl_listener *listener, void *data) + } + } + ++ /* Update bar */ ++ if (stext[0] == '\0') ++ strncpy(stext, "dwl-"VERSION, sizeof(stext)); ++ wl_list_for_each(m, &mons, link) { ++ updatebar(m); ++ drawbar(m); ++ } ++ + /* FIXME: figure out why the cursor image is at 0,0 after turning all + * the monitors on. + * Move the cursor image where it used to be. It does not generate a +@@ -2859,12 +3060,36 @@ updatemons(struct wl_listener *listener, void *data) + wlr_output_manager_v1_set_configuration(output_mgr, config); + } + ++void ++updatebar(Monitor *m) ++{ ++ int rw, rh; ++ char fontattrs[12]; ++ ++ wlr_output_transformed_resolution(m->wlr_output, &rw, &rh); ++ m->b.width = rw; ++ m->b.real_width = (int)((float)m->b.width / m->wlr_output->scale); ++ ++ if (m->b.scale == m->wlr_output->scale && m->drw) ++ return; ++ ++ drwl_destroy_font(m->drw->font); ++ snprintf(fontattrs, sizeof(fontattrs), "dpi=%.2f", 96. * m->wlr_output->scale); ++ if (!(drwl_load_font(m->drw, LENGTH(fonts), fonts, fontattrs))) ++ die("Could not load font"); ++ ++ m->b.scale = m->wlr_output->scale; ++ m->lrpad = m->drw->font->height; ++ m->b.height = m->drw->font->height + 2; ++ m->b.real_height = (int)((float)m->b.height / m->wlr_output->scale); ++} ++ + void + updatetitle(struct wl_listener *listener, void *data) + { + Client *c = wl_container_of(listener, c, set_title); + if (c == focustop(c->mon)) +- printstatus(); ++ drawbars(); + } + + void +@@ -2877,10 +3102,10 @@ urgent(struct wl_listener *listener, void *data) + return; + + c->isurgent = 1; +- printstatus(); ++ drawbars(); + + if (client_surface(c)->mapped) +- client_set_border_color(c, urgentcolor); ++ client_set_border_color(c, (float[])COLOR(colors[SchemeUrg][ColBorder])); + } + + void +@@ -2893,7 +3118,7 @@ view(const Arg *arg) + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; + focusclient(focustop(selmon), 1); + arrange(selmon); +- printstatus(); ++ drawbars(); + } + + void +@@ -2934,6 +3159,7 @@ xytonode(double x, double y, struct wlr_surface **psurface, + { + struct wlr_scene_node *node, *pnode; + struct wlr_surface *surface = NULL; ++ struct wlr_scene_surface *scene_surface = NULL; + Client *c = NULL; + LayerSurface *l = NULL; + int layer; +@@ -2942,9 +3168,12 @@ xytonode(double x, double y, struct wlr_surface **psurface, + if (!(node = wlr_scene_node_at(&layers[layer]->node, x, y, nx, ny))) + continue; + +- if (node->type == WLR_SCENE_NODE_BUFFER) +- surface = wlr_scene_surface_try_from_buffer( +- wlr_scene_buffer_from_node(node))->surface; ++ if (node->type == WLR_SCENE_NODE_BUFFER) { ++ scene_surface = wlr_scene_surface_try_from_buffer( ++ wlr_scene_buffer_from_node(node)); ++ if (!scene_surface) continue; ++ surface = scene_surface->surface; ++ } + /* Walk the tree to find a node that knows the client */ + for (pnode = node; pnode && !c; pnode = &pnode->parent->node) + c = pnode->data; +@@ -3083,10 +3312,10 @@ sethints(struct wl_listener *listener, void *data) + return; + + c->isurgent = xcb_icccm_wm_hints_get_urgency(c->surface.xwayland->hints); +- printstatus(); ++ drawbars(); + + if (c->isurgent && surface && surface->mapped) +- client_set_border_color(c, urgentcolor); ++ client_set_border_color(c, (float[])COLOR(colors[SchemeUrg][ColBorder])); + } + + void +-- +2.45.2 + diff --git a/patches/bar/bar.png b/patches/bar/bar.png new file mode 100644 index 0000000..276256b Binary files /dev/null and b/patches/bar/bar.png differ