greenhouse

kore

my public infrastructure monorepo

git clone [email protected]:kore.git

feat(*): garden theme for peony

commit1630e5b594ace57fc34c41f1ef4d3b0cc8cb91f3
parenta8a6a2a7529b5ee3db8d500f1024da22f6ae2f0c
authorlostduk <[email protected]>
date2026-10-15 14:57 +0000
flake.nix3+
home/features/cli/nvim.nix1+
home/features/cli/tmux.nix16+
home/features/wayland/foot.nix35++
home/features/wayland/wbg.nix34++
home/profiles/peony.nix1+
nixos/roles/desktop.nix40++
overlays/default.nix23+
overlays/dwl/bar.patch1270++++++++++++++++++++++++++++++++++++++++
overlays/dwl/config.h50++
overlays/dwl/gaps.patch125++++
overlays/dwl/garden.patch95+++
palette.nix28+

13 files changed, -23/+1698

flake.nix

@@ -22,8 +22,9 @@       inherit (self) outputs;       inherit (nixpkgs) lib;       kore = lib.importJSON ./kore.json;+      palette = import ./palette.nix;     in {-      inherit kore;+      inherit kore palette;        nixosConfigurations = import ./nixos/hosts { inherit inputs outputs lib kore; };       homeModules = import ./home/profiles { inherit kore; };

home/features/cli/nvim.nix

@@ -35,6 +35,7 @@       set number       set relativenumber       set nowrap+      set notermguicolors       set lazyredraw       set cc=80 

home/features/cli/tmux.nix

@@ -1,9 +1,23 @@-{+{ outputs, ... }:++let+  c = outputs.palette.colors;+in {   programs.tmux = {     enable = true;      prefix = "C-x";     terminal = "tmux-256color";     keyMode = "vi";++    extraConfig = ''+      set -g status-style "bg=${c.bg},fg=${c.fg}"+      set -g window-status-style "fg=${c.dim}"+      set -g window-status-current-style "fg=${c.green}"+      set -g pane-border-style "fg=${c.surface}"+      set -g pane-active-border-style "fg=${c.green}"+      set -g message-style "bg=${c.bgAlt},fg=${c.fg}"+      set -g mode-style "bg=${c.surface},fg=${c.fg}"+    '';   }; }

home/features/wayland/foot.nix

@@ -1,11 +1,42 @@-{+{ lib, outputs, ... }:++let+  c = lib.mapAttrs (_: lib.removePrefix "#") outputs.palette.colors;+in {   programs.foot = {     enable = true;     settings = {       main = {-        font = "ComicShannsMono Nerd Font:size=11";+        font = "Fantasque Sans Mono:size=11";         line-height = "16px";       };++      colors-dark = {+        foreground = c.fg;+        background = c.bg;+        cursor = "${c.bg} ${c.fg}";++        selection-foreground = c.fg;+        selection-background = c.surface;++        regular0 = c.bgAlt;+        regular1 = c.red;+        regular2 = c.green;+        regular3 = c.yellow;+        regular4 = c.blue;+        regular5 = c.magenta;+        regular6 = c.teal;+        regular7 = c.dim;++        bright0 = c.surface;+        bright1 = c.red;+        bright2 = c.green;+        bright3 = c.yellow;+        bright4 = c.blue;+        bright5 = c.magenta;+        bright6 = c.teal;+        bright7 = c.fg;+      };     };   }; }

home/features/wayland/wbg.nix

@@ -0,0 +1,34 @@+{ lib, pkgs, ... }:++let+  wbg = pkgs.wbg.override { enableWebp = false; };++  wallpaper = pkgs.runCommand "jardin-blanc.jpg" {+    nativeBuildInputs = [ pkgs.imagemagick ];+    src = pkgs.fetchurl {+      name = "le-jardin-blanc-au-crepuscule.jpg";+      url = "https://upload.wikimedia.org/wikipedia/commons/e/e2/Le_jardin_blanc_au_cr%C3%A9puscule.jpg";+      hash = "sha256-JEJ0htoF0D/Pu1UxUzSPseTTrBXKSpMlc2DYnpthERA=";+      curlOptsList = [ "--user-agent" "kore-nixos-config/1.0" ];+    };+  } ''+    magick "$src" \+      -distort Perspective '179,184 0,0  2830,184 2651,0  189,1891 0,1707  2832,1891 2651,1707' \+      -crop 2651x1707+0+0 +repage \+      -modulate 92,85 \+      -resize 1920x -gravity south -crop 1920x1080+0+38 +repage \+      -strip -quality 90 "jpg:$out"+  '';+in {+  systemd.user.services.wbg = {+    Unit = {+      Description = "wbg wallpaper";+      PartOf = [ "graphical-session.target" ];+      After = [ "graphical-session.target" ];+    };++    Service.ExecStart = "${lib.getExe wbg} ${wallpaper}";++    Install.WantedBy = [ "dwl-session.target" ];+  };+}

home/profiles/peony.nix

@@ -12,6 +12,7 @@      ../features/wayland/dwl.nix     ../features/wayland/foot.nix+    ../features/wayland/wbg.nix      ../features/secrets.nix   ];

nixos/roles/desktop.nix

@@ -1,11 +1,37 @@-{ pkgs, ... }:--{-  programs.dwl.enable = true;+{ lib, pkgs, ... }:++let+  garden-status = pkgs.writeShellApplication {+    name = "garden-status";+    runtimeInputs = [ pkgs.coreutils ];+    text = ''+      while :; do+        case "$(date +%-H)" in+          5|6) tod=dawn ;;+          7|8|9|10) tod=morning ;;+          11|12|13) tod=noon ;;+          14|15|16) tod=afternoon ;;+          17|18) tod=dusk ;;+          19|20|21) tod=eventide ;;+          *) tod=night ;;+        esac+        line="$(date "+%a %-d %b · $tod %H:%M")"+        printf '%s\n' "''${line,,}"+        sleep "$((60 - $(date +%-S)))"+      done+    '';+  };+in {+  programs.dwl = {+    enable = true;+    extraSessionCommands = "exec < <(${lib.getExe garden-status})";+  };    environment.loginShellInit = ''     if [ -z "$WAYLAND_DISPLAY" ] && [ "$(tty)" = /dev/tty1 ]; then-      exec /etc/xdg/dwl-session+      /etc/xdg/dwl-session+      systemctl --user stop dwl-session.target+      exit     fi   ''; @@ -37,7 +63,7 @@       noto-fonts-cjk-serif       noto-fonts-color-emoji -      nerd-fonts.comic-shanns-mono+      fantasque-sans-mono     ];      fontconfig = {@@ -46,7 +72,7 @@       defaultFonts = {         serif = [ "Noto Serif" "Noto Color Emoji" ];         sansSerif = [ "Noto Sans" "Noto Color Emoji" ];-        monospace = [ "ComicShannsMono Nerd Font" "Noto Color Emoji" ];+        monospace = [ "Fantasque Sans Mono" "Noto Color Emoji" ];         emoji = [ "Noto Color Emoji" ];       };     };

overlays/default.nix

@@ -1,4 +1,4 @@-{ inputs, ... }:+{ inputs, outputs, ... }:  {   # https://m7.rs/git/nix-config/tree/overlays/default.nix#line-13@@ -16,6 +16,25 @@   };    modifications = final: prev: {-    dwl = prev.dwl.override { configH = ./dwl_config.h; };+    dwl =+      let+        inherit (final) lib;+        hex = c: lib.removePrefix "#" c;+        colors = lib.getAttrs [ "bg" "fg" "surface" "green" "red" "teal" ] outputs.palette.colors;+      in+        (prev.dwl.override {+          configH = final.replaceVars ./dwl/config.h (+            lib.mapAttrs (_: c: "0x${hex c}ff") colors+            // lib.mapAttrs' (n: c: lib.nameValuePair "${n}Rgb" (hex c))+              (lib.getAttrs [ "bg" "fg" "surface" "green" ] colors)+          );+        }).overrideAttrs (old: {+          patches = (old.patches or [ ]) ++ [+            ./dwl/bar.patch+            ./dwl/gaps.patch+            ./dwl/garden.patch+          ];+          buildInputs = old.buildInputs ++ [ final.fcft final.pixman final.libdrm ];+        });   }; }

overlays/dwl/bar.patch

@@ -0,0 +1,1270 @@+From 77b43f10b942abe9caab8a91d3a82923b1eb231c Mon Sep 17 00:00:00 2001+From: sewn <[email protected]>+Date: Mon, 5 Jan 2026 16:51:14 +0300+Subject: [PATCH] Implement dwm bar clone++---+ Makefile     |   2 +-+ config.def.h |  33 ++--+ drwl.h       | 310 +++++++++++++++++++++++++++++++++++++ dwl.c        | 441 +++++++++++++++++++++++++++++++++++++++++----------+ 4 files changed, 696 insertions(+), 90 deletions(-)+ create mode 100644 drwl.h++diff --git a/Makefile b/Makefile+index 8304245..09ae285 100644+--- a/Makefile++++ b/Makefile+@@ -12,7 +12,7 @@ DWLDEVCFLAGS = -g -Wpedantic -Wall -Wextra -Wdeclaration-after-statement \+ 	-Wfloat-conversion+ + # CFLAGS / LDFLAGS+-PKGS      = wayland-server xkbcommon libinput $(XLIBS)++PKGS      = wayland-server xkbcommon libinput pixman-1 fcft $(XLIBS)+ DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(WLR_INCS) $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS)+ LDLIBS    = `$(PKG_CONFIG) --libs $(PKGS)` $(WLR_LIBS) -lm $(LIBS)+ +diff --git a/config.def.h b/config.def.h+index 13c5322..1fc6203 100644+--- a/config.def.h++++ b/config.def.h+@@ -8,15 +8,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 unsigned int snap             = 32; /* snap pixel */+-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.0f, 0.0f, 0.0f, 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)++/* tagging */++static char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };+ + /* logging */+ static int log_level = WLR_ERROR;+@@ -123,6 +129,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} },+@@ -166,9 +173,17 @@ 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} },+ };+ + static const Axis axes[] = {+diff --git a/drwl.h b/drwl.h+new file mode 100644+index 0000000..21afd21+--- /dev/null++++ b/drwl.h+@@ -0,0 +1,310 @@++/*++ * drwl - https://codeberg.org/sewn/drwl++ *++ * Copyright (c) 2023-2025 sewn <[email protected]>++ * Copyright (c) 2024 notchoc <[email protected]>++ * ++ * 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.++ *++ * The UTF-8 Decoder included is from Bjoern Hoehrmann:++ * Copyright (c) 2008-2010 Bjoern Hoehrmann <[email protected]>++ * See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.++ */++#pragma once++++#include <stdlib.h>++#include <fcft/fcft.h>++#include <pixman-1/pixman.h>++++enum { ColFg, ColBg, ColBorder }; /* colorscheme index */++++typedef struct fcft_font Fnt;++typedef pixman_image_t Img;++++typedef struct {++	Img *image;++	Fnt *font;++	uint32_t *scheme;++} Drwl;++++#define UTF8_ACCEPT 0++#define UTF8_REJECT 12++#define UTF8_INVALID 0xFFFD++++static const uint8_t utf8d[] = {++	 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,++	 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,++	 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,++	 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,++	 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,  9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,++	 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,++	 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,++	10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,++++	 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,++	12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,++	12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,++	12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,++	12,36,12,12,12,12,12,12,12,12,12,12,++};++++static inline uint32_t++utf8decode(uint32_t *state, uint32_t *codep, uint8_t byte)++{++	uint32_t type = utf8d[byte];++++	*codep = (*state != UTF8_ACCEPT) ?++		(byte & 0x3fu) | (*codep << 6) :++		(0xff >> type) & (byte);++++	*state = utf8d[256 + *state + type];++	return *state;++}++++static int++drwl_init(void)++{++	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, Fnt *font)++{++	if (drwl)++		drwl->font = font;++}++++static void++drwl_setimage(Drwl *drwl, Img *image)++{++	if (drwl)++		drwl->image = image;++}++++static Fnt *++drwl_font_create(Drwl *drwl, size_t count,++		const char *names[static count], const char *attributes)++{++	Fnt *font = fcft_from_name(count, names, attributes);++	if (drwl)++		drwl_setfont(drwl, font);++	return font;++}++++static void++drwl_font_destroy(Fnt *font)++{++	fcft_destroy(font);++}++++static inline pixman_color_t++convert_color(uint32_t clr)++{++	return (pixman_color_t){++		((clr >> 24) & 0xFF) * 0x101 * (clr & 0xFF) / 0xFF,++		((clr >> 16) & 0xFF) * 0x101 * (clr & 0xFF) / 0xFF,++		((clr >> 8) & 0xFF) * 0x101 * (clr & 0xFF) / 0xFF,++		(clr & 0xFF) * 0x101++	};++}++++static void++drwl_setscheme(Drwl *drwl, uint32_t *scm)++{++	if (drwl)++		drwl->scheme = scm;++}++++static Img *++drwl_image_create(Drwl *drwl, unsigned int w, unsigned int h, uint32_t *bits)++{++	Img *image;++	pixman_region32_t clip;++++	image = pixman_image_create_bits_no_clear(++		PIXMAN_a8r8g8b8, w, h, bits, w * 4);++	if (!image)++		return NULL;++	pixman_region32_init_rect(&clip, 0, 0, w, h);++	pixman_image_set_clip_region32(image, &clip);++	pixman_region32_fini(&clip);++++	if (drwl)++		drwl_setimage(drwl, image);++	return image;++}++++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->image)++		return;++++	clr = convert_color(drwl->scheme[invert ? ColBg : ColFg]);++	if (filled)++		pixman_image_fill_rectangles(PIXMAN_OP_SRC, drwl->image, &clr, 1,++			&(pixman_rectangle16_t){x, y, w, h});++	else++		pixman_image_fill_rectangles(PIXMAN_OP_SRC, drwl->image, &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 render = x || y || w || h;++	long x_kern;++	uint32_t cp = 0, last_cp = 0, state;++	pixman_color_t clr;++	pixman_image_t *fg_pix = NULL;++	int noellipsis = 0;++	const struct fcft_glyph *glyph, *eg = NULL;++	int fcft_subpixel_mode = FCFT_SUBPIXEL_DEFAULT;++++	if (!drwl || (render && (!drwl->scheme || !w || !drwl->image)) || !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;++++	if (render)++		eg = fcft_rasterize_char_utf32(drwl->font, 0x2026 /* … */, fcft_subpixel_mode);++++	for (const char *p = text, *pp; pp = p, *p; p++) {++		for (state = UTF8_ACCEPT; *p &&++		     utf8decode(&state, &cp, *p) > UTF8_REJECT; p++)++			;++		if (!*p || state == UTF8_REJECT) {++			cp = UTF8_INVALID;++			if (p > pp)++				p--;++		}++++		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;++++		if (render && !noellipsis && x_kern + glyph->advance.x + eg->advance.x > w &&++		    *(p + 1) != '\0') {++			/* cannot fit ellipsis after current codepoint */++			if (drwl_text(drwl, 0, 0, 0, 0, 0, pp, 0) + x_kern <= w) {++				noellipsis = 1;++			} else {++				w -= eg->advance.x;++				pixman_image_composite32(++					PIXMAN_OP_OVER, fg_pix, eg->pix, drwl->image, 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->image, 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->image, 0, 0, 0, 0,++				x + glyph->x, ty - glyph->y, glyph->width, glyph->height);++++		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_image_destroy(Img *image)++{++	pixman_image_unref(image);++}++++static void++drwl_destroy(Drwl *drwl)++{++	if (drwl->font)++		drwl_font_destroy(drwl->font);++	if (drwl->image)++		drwl_image_destroy(drwl->image);++	free(drwl);++}++++static void++drwl_fini(void)++{++	fcft_fini();++}+diff --git a/dwl.c b/dwl.c+index 4cfd0fd..751338b 100644+--- a/dwl.c++++ b/dwl.c+@@ -1,11 +1,13 @@+ /*+  * See LICENSE file for copyright and license details.+  */++#include <fcntl.h>+ #include <getopt.h>+ #include <libinput.h>+ #include <limits.h>+ #include <linux/input-event-codes.h>+ #include <math.h>++#include <libdrm/drm_fourcc.h>+ #include <signal.h>+ #include <stdio.h>+ #include <stdlib.h>+@@ -66,6 +68,7 @@+ #include <wlr/types/wlr_xdg_decoration_v1.h>+ #include <wlr/types/wlr_xdg_output_v1.h>+ #include <wlr/types/wlr_xdg_shell.h>++#include <wlr/interfaces/wlr_buffer.h>+ #include <wlr/util/log.h>+ #include <wlr/util/region.h>+ #include <xkbcommon/xkbcommon.h>+@@ -77,20 +80,24 @@+ + #include "xdg-shell-protocol.h"+ #include "util.h"++#include "drwl.h"+ + /* macros */+ #define CLEANMASK(mask)         (mask & ~WLR_MODIFIER_CAPS)+ #define VISIBLEON(C, M)         ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags]))+-#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 { struct wl_listener *_l = ecalloc(1, sizeof(*_l)); _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, LyrUnmanaged, LyrOverlay,+ 	LyrIMPopup, LyrBlock, NUM_LAYERS }; /* scene layers */+ enum { AxisUp, AxisRight, AxisDown, AxisLeft };++enum { ClkTagBar, ClkLtSymbol, ClkStatus, ClkTitle, ClkClient, ClkRoot }; /* clicks */+ + typedef union {+ 	int i;+@@ -107,6 +114,7 @@ typedef struct {+ } Axis;+ + typedef struct {++	unsigned int click;+ 	unsigned int mod;+ 	unsigned int button;+ 	void (*func)(const Arg *);+@@ -203,10 +211,19 @@ typedef struct {+ 	void (*arrange)(Monitor *);+ } Layout;+ ++typedef struct {++	struct wlr_buffer base;++	struct wl_listener release;++	bool busy;++	Img *image;++	uint32_t data[];++} Buffer;+++ 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;+@@ -214,6 +231,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];+@@ -225,6 +247,9 @@ struct Monitor {+ 	int nmaster;+ 	char ltsymbol[16];+ 	int asleep;++	Drwl *drw;++	Buffer *pool[2];++	int lrpad;+ };+ + typedef struct {+@@ -267,6 +292,13 @@ 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 baracceptsinput(struct wlr_scene_buffer *buffer, double *sx, double *sy);++static void bufdestroy(struct wlr_buffer *buffer);++static bool bufdatabegin(struct wlr_buffer *buffer, uint32_t flags,++		void **data, uint32_t *format, size_t *stride);++static void bufdataend(struct wlr_buffer *buffer);++static Buffer *bufmon(Monitor *m);++static void bufrelease(struct wl_listener *listener, void *data);+ static void buttonpress(struct wl_listener *listener, void *data);+ static void capturerequest(struct wl_listener *listener, void *data);+ static void chvt(const Arg *arg);+@@ -303,6 +335,8 @@ static void destroypointerconstraint(struct wl_listener *listener, void *data);+ static void destroysessionlock(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);+@@ -331,7 +365,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, LayerSurface* l, 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);+@@ -352,9 +385,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 statusin(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);+@@ -363,6 +398,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);+@@ -431,6 +467,15 @@ static struct wl_list mons;+ static Monitor *selmon;+ static int consumed[KEY_MAX + 1];+ ++static char stext[256];++static struct wl_event_source *status_event_source;++++static const struct wlr_buffer_impl buffer_impl = {++    .destroy = bufdestroy,++    .begin_data_ptr_access = bufdatabegin,++    .end_data_ptr_access = bufdataend,++};+++ /* global event handlers */+ static struct wl_listener cursor_axis = {.notify = axisnotify};+ static struct wl_listener cursor_button = {.notify = buttonpress};+@@ -565,7 +610,7 @@ arrange(Monitor *m)+ 	wlr_scene_node_set_enabled(&m->fullscreen_bg->node,+ 			(c = focustop(m)) && c->isfullscreen);+ +-	strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol));++	strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof(m->ltsymbol));+ + 	/* We move all clients (except fullscreen and unmanaged) to LyrTile while+ 	 * in floating layout to avoid "real" floating clients be always on top */+@@ -620,6 +665,11 @@ arrangelayers(Monitor *m)+ 	if (!m->wlr_output->enabled)+ 		return;+ ++	if (m->scene_buffer->node.enabled) {++		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);+@@ -682,18 +732,103 @@ axisnotify(struct wl_listener *listener, void *data)+ 			event->delta_discrete, event->source, event->relative_direction);+ }+ ++bool++baracceptsinput(struct wlr_scene_buffer *buffer, double *sx, double *sy)++{++	return true;++}++++void++bufdestroy(struct wlr_buffer *wlr_buffer)++{++	Buffer *buf = wl_container_of(wlr_buffer, buf, base);++	if (buf->busy)++		wl_list_remove(&buf->release.link);++	drwl_image_destroy(buf->image);++	free(buf);++}++++bool++bufdatabegin(struct wlr_buffer *wlr_buffer, uint32_t flags,++		void **data, uint32_t *format, size_t *stride)++{++	Buffer *buf = wl_container_of(wlr_buffer, buf, base);++++	if (flags & WLR_BUFFER_DATA_PTR_ACCESS_WRITE) return false;++++	*data   = buf->data;++	*stride = wlr_buffer->width * 4;++	*format = DRM_FORMAT_ARGB8888;++++	return true;++}++++void++bufdataend(struct wlr_buffer *wlr_buffer)++{++}++++Buffer *++bufmon(Monitor *m)++{++	size_t i;++	Buffer *buf = NULL;++++	for (i = 0; i < LENGTH(m->pool); i++) {++		if (m->pool[i]) {++			if (m->pool[i]->busy)++				continue;++			buf = m->pool[i];++			break;++		}++++		buf = ecalloc(1, sizeof(Buffer) + (m->b.width * 4 * m->b.height));++		buf->image = drwl_image_create(NULL, m->b.width, m->b.height, buf->data);++		wlr_buffer_init(&buf->base, &buffer_impl, m->b.width, m->b.height);++		m->pool[i] = buf;++		break;++	}++	if (!buf)++		return NULL;++++	buf->busy = true;++	LISTEN(&buf->base.events.release, &buf->release, bufrelease);++	wlr_buffer_lock(&buf->base);++	drwl_setimage(m->drw, buf->image);++	return buf;++}++++void++bufrelease(struct wl_listener *listener, void *data)++{++	Buffer *buf = wl_container_of(listener, buf, release);++	buf->busy = false;++	wl_list_remove(&buf->release.link);++}+++ void+ buttonpress(struct wl_listener *listener, void *data)+ {++	unsigned int i = 0, x = 0;++	double cx;++	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 = NULL;+ 	LayerSurface *l = NULL;+ 	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;+++ 	switch (event->state) {+ 	case WL_POINTER_BUTTON_STATE_PRESSED:+ 		cursor_mode = CurPressed;+@@ -701,10 +836,28 @@ buttonpress(struct wl_listener *listener, void *data)+ 		if (locked)+ 			break;+ ++		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) {++			cx = (cursor->x - selmon->m.x) * selmon->wlr_output->scale;++			do++				x += TEXTW(selmon, tags[i]);++			while (cx >= x && ++i < LENGTH(tags));++			if (i < LENGTH(tags)) {++				click = ClkTagBar;++				arg.ui = 1 << i;++			} else if (cx < x + TEXTW(selmon, selmon->ltsymbol))++				click = ClkLtSymbol;++			else if (cx > selmon->b.width - (TEXTW(selmon, stext) - selmon->lrpad + 2)) {++				click = ClkStatus;++			} else++				click = ClkTitle;++		}+++ 		/* Change focus if the button was _pressed_ over a client+ 		   or a layer surface with on-demand keyboard interactivity */+ 		xytonode(cursor->x, cursor->y, NULL, &c, &l, NULL, NULL);+-		if (c && (!client_is_unmanaged(c) || client_wants_focus(c))) {++		if (click == ClkClient && c && (!client_is_unmanaged(c) || client_wants_focus(c))) {+ 			focusclient(c, 1);+ 		} else if (l && l->layer_surface->current.keyboard_interactive) {+ 			focusclient(NULL, 0);+@@ -714,9 +867,8 @@ buttonpress(struct wl_listener *listener, void *data)+ 		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;+ 			}+ 		}+@@ -814,6 +966,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+@@ -829,6 +983,12 @@ cleanupmon(struct wl_listener *listener, void *data)+ 			wlr_layer_surface_v1_destroy(l->layer_surface);+ 	}+ ++	for (i = 0; i < LENGTH(m->pool); i++)++		wlr_buffer_drop(&m->pool[i]->base);++++	drwl_setimage(m->drw, NULL);++	drwl_destroy(m->drw);+++ 	wl_list_remove(&m->destroy.link);+ 	wl_list_remove(&m->frame.link);+ 	wl_list_remove(&m->link);+@@ -841,6 +1001,7 @@ cleanupmon(struct wl_listener *listener, void *data)+ + 	closemon(m);+ 	wlr_scene_node_destroy(&m->fullscreen_bg->node);++	wlr_scene_node_destroy(&m->scene_buffer->node);+ 	free(m);+ }+ +@@ -908,7 +1069,7 @@ closemon(Monitor *m)+ 			setmon(c, selmon, c->tags);+ 	}+ 	focusclient(focustop(selmon), 1);+-	printstatus();++	drawbars();+ }+ + void+@@ -1164,7 +1325,7 @@ createmon(struct wl_listener *listener, void *data)+ 			m->nmaster = r->nmaster;+ 			m->lt[0] = r->lt;+ 			m->lt[1] = &layouts[LENGTH(layouts) > 1 && r->lt != &layouts[1]];+-			strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol));++			strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof(m->ltsymbol));+ 			wlr_output_state_set_scale(&state, r->scale);+ 			wlr_output_state_set_transform(&state, r->rr);+ 			break;+@@ -1186,8 +1347,15 @@ 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 = baracceptsinput;++	updatebar(m);+++ 	wl_list_insert(&mons, &m->link);+-	printstatus();++	drawbars();+ + 	/* The xdg-protocol specifies:+ 	 *+@@ -1504,6 +1672,80 @@ 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;++	Client *c;++	Buffer *buf;++++	if (!m->scene_buffer->node.enabled)++		return;++	if (!(buf = bufmon(m)))++		return;++++	/* 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);++		}++	}++++	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_unlock(&buf->base);++}++++void++drawbars(void)++{++	Monitor *m = NULL;++++	wl_list_for_each(m, &mons, link)++		drawbar(m);++}+++ void+ focusclient(Client *c, int lift)+ {+@@ -1541,7 +1783,7 @@ 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]));+ 	}+ + 	/* If an overlay is focused, don't focus or activate the client,+@@ -1557,12 +1799,12 @@ focusclient(Client *c, int lift)+ 	if (focused_client && focused_client != c && !(c && client_is_unmanaged(c))) {+ 		struct wlr_surface *s = client_surface(focused_client);+ 		if (c)+-			client_set_border_color(focused_client, bordercolor);++			client_set_border_color(focused_client, (float[])COLOR(colors[SchemeNorm][ColBorder]));+ 		if (s && s->mapped)+ 			client_activate_surface(s, 0);+ 		focused_client = NULL;+ 	}+-	printstatus();++	drawbars();+ + 	if (!c) {+ 		/* With no client, all we have left is to clear focus */+@@ -1898,7 +2140,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;+ 	}+ +@@ -1932,7 +2174,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);+@@ -2253,44 +2495,6 @@ pointerfocus(Client *c, LayerSurface *l, struct wlr_surface *surface, double sx,+ 	wlr_seat_pointer_notify_motion(seat, time, sx, sy);+ }+ +-void+-printstatus(void)+-{+-	Monitor *m = NULL;+-	Client *c;+-	uint32_t occ, urg, sel;+-+-	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))) {+-			printf("%s title %s\n", m->wlr_output->name, client_get_title(c));+-			printf("%s appid %s\n", m->wlr_output->name, client_get_appid(c));+-			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)+ {+@@ -2408,22 +2612,14 @@ 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]);+ 	}+ + 	/* Mark stdout as non-blocking to avoid the startup script+@@ -2433,7 +2629,7 @@ run(char *startup_cmd)+ 	if (fd_set_nonblock(STDOUT_FILENO) < 0)+ 		close(STDOUT_FILENO);+ +-	printstatus();++	drawbars();+ + 	/* At this point the outputs are initialized, choose initial selmon based on+ 	 * cursor position, and set default cursor image */+@@ -2499,7 +2695,7 @@ setfloating(Client *c, int floating)+ 			(p && p->isfullscreen) ? LyrFS+ 			: c->isfloating ? LyrFloat : LyrTile]);+ 	arrange(c->mon);+-	printstatus();++	drawbars();+ }+ + void+@@ -2522,7 +2718,7 @@ setfullscreen(Client *c, int fullscreen)+ 		resize(c, c->prev, 0);+ 	}+ 	arrange(c->mon);+-	printstatus();++	drawbars();+ }+ + void+@@ -2534,9 +2730,9 @@ setlayout(const Arg *arg)+ 		selmon->sellt ^= 1;+ 	if (arg && arg->v)+ 		selmon->lt[selmon->sellt] = (Layout *)arg->v;+-	strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, LENGTH(selmon->ltsymbol));++	strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof(selmon->ltsymbol));+ 	arrange(selmon);+-	printstatus();++	drawbar(selmon);+ }+ + /* arg > 1.0 will set mfact absolutely */+@@ -2609,6 +2805,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+@@ -2816,6 +3013,11 @@ setup(void)+ 	text_input_manager = wlr_text_input_manager_v3_create(dpy);+ 	input_method_relay = input_method_relay_create();+ ++	drwl_init();++++	status_event_source = wl_event_loop_add_fd(wl_display_get_event_loop(dpy),++		STDIN_FILENO, WL_EVENT_READABLE, statusin, 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 */+@@ -2840,6 +3042,8 @@ void+ spawn(const Arg *arg)+ {+ 	if (fork() == 0) {++		close(STDIN_FILENO);++		open("/dev/null", O_RDWR);+ 		dup2(STDERR_FILENO, STDOUT_FILENO);+ 		setsid();+ 		execvp(((char **)arg->v)[0], (char **)arg->v);+@@ -2858,6 +3062,30 @@ startdrag(struct wl_listener *listener, void *data)+ 	LISTEN_STATIC(&drag->icon->events.destroy, destroydragicon);+ }+ ++int++statusin(int fd, unsigned int mask, void *data)++{++	char status[256];++	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)+ {+@@ -2868,7 +3096,7 @@ tag(const Arg *arg)+ 	sel->tags = arg->ui & TAGMASK;+ 	focusclient(focustop(selmon), 1);+ 	arrange(selmon);+-	printstatus();++	drawbars();+ }+ + void+@@ -2913,6 +3141,15 @@ tile(Monitor *m)+ 	}+ }+ ++void++togglebar(const Arg *arg)++{++	wlr_scene_node_set_enabled(&selmon->scene_buffer->node,++		!selmon->scene_buffer->node.enabled);++	arrangelayers(selmon);++	drawbars();++}+++ void+ togglefloating(const Arg *arg)+ {+@@ -2941,7 +3178,7 @@ toggletag(const Arg *arg)+ 	sel->tags = newtags;+ 	focusclient(focustop(selmon), 1);+ 	arrange(selmon);+-	printstatus();++	drawbars();+ }+ + void+@@ -2954,7 +3191,7 @@ toggleview(const Arg *arg)+ 	selmon->tagset[selmon->seltags] = newtagset;+ 	focusclient(focustop(selmon), 1);+ 	arrange(selmon);+-	printstatus();++	drawbars();+ }+ + void+@@ -3019,7 +3256,7 @@ unmapnotify(struct wl_listener *listener, void *data)+ 	c->image_capture_source = NULL;+ 	wlr_scene_node_destroy(&c->scene->node);+ 	client_surface(c)->data = NULL;+-	printstatus();++	drawbars();+ 	motionnotify(0, NULL, 0, 0, 0, 0);+ }+ +@@ -3122,6 +3359,13 @@ updatemons(struct wl_listener *listener, void *data)+ 		}+ 	}+ ++	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+@@ -3132,12 +3376,45 @@ updatemons(struct wl_listener *listener, void *data)+ 	wlr_output_manager_v1_set_configuration(output_mgr, config);+ }+ ++void++updatebar(Monitor *m)++{++	size_t i;++	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);++++	wlr_scene_node_set_enabled(&m->scene_buffer->node, m->wlr_output->enabled ? showbar : 0);++++	for (i = 0; i < LENGTH(m->pool); i++)++		if (m->pool[i]) {++			wlr_buffer_drop(&m->pool[i]->base);++			m->pool[i] = NULL;++		}++++	if (m->b.scale == m->wlr_output->scale && m->drw)++		return;++++	drwl_font_destroy(m->drw->font);++	snprintf(fontattrs, sizeof(fontattrs), "dpi=%.2f", 96. * m->wlr_output->scale);++	if (!(drwl_font_create(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();+ + 	if (c->foreign_toplevel_handle) {+ 		struct wlr_ext_foreign_toplevel_handle_v1_state foreign_toplevel_state = {+@@ -3159,10 +3436,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+@@ -3175,7 +3452,7 @@ view(const Arg *arg)+ 		selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;+ 	focusclient(focustop(selmon), 1);+ 	arrange(selmon);+-	printstatus();++	drawbars();+ }+ + void+@@ -3219,6 +3496,7 @@ xytonode(double x, double y, struct wlr_surface **psurface,+ {+ 	struct wlr_scene_node *node;+ 	struct wlr_surface *surface = NULL;++	struct wlr_scene_surface *scene_surface = NULL;+ 	int layer;+ + 	for (layer = NUM_LAYERS - 1; !surface && layer >= 0; layer--) {+@@ -3226,9 +3504,12 @@ xytonode(double x, double y, struct wlr_surface **psurface,+ 				!(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;++		}+ 	}+ + 	if (psurface) *psurface = surface;+@@ -3362,10 +3643,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.55.0+

overlays/dwl_config.h → overlays/dwl/config.h

@@ -1,5 +1,10 @@+#define COLOR(hex) { ((hex >> 24) & 0xFF) / 255.0f, \+                     ((hex >> 16) & 0xFF) / 255.0f, \+                     ((hex >> 8) & 0xFF) / 255.0f, \+                     (hex & 0xFF) / 255.0f }+ /* general */-#define TAGCOUNT (6)+static char *tags[] = { "❦", "✾", "❁", "☘", "❃", "❋" };  static int log_level                       = WLR_ERROR; static const int sloppyfocus               = 1;@@ -8,11 +13,26 @@ static const int bypass_surface_visibility = 0; /* appearance */ static const unsigned int borderpx         = 1; static const unsigned int snap             = 32;-static const float rootcolor[]             = {0.1f, 0.1f, 0.1f, 1.0f};-static const float bordercolor[]           = {0.2f, 0.2f, 0.2f, 1.0f};-static const float focuscolor[]            = {0.0f, 0.3f, 0.4f, 1.0f};-static const float urgentcolor[]           = {0.1f, 0.0f, 0.0f, 1.0f};-static const float fullscreen_bg[]         = {0.1f, 0.1f, 0.1f, 1.0f};+static const unsigned int gappx            = 10;+static const int smartgaps                 = 0;+static int gaps                            = 1;+static const int showbar                   = 1;+static const int topbar                    = 1;+static const char *fonts[]                 = { "Fantasque Sans Mono:size=11", "Noto Sans Symbols 2:size=13" };+static const float rootcolor[]             = COLOR(@bg@);+static const float fullscreen_bg[]         = COLOR(@bg@);++static uint32_t colors[][3] = {+	/*                fg          bg     border */+	[SchemeNorm]  = { @fg@,      @bg@, @surface@ },+	[SchemeSel]   = { @green@,   @bg@, @green@ },+	[SchemeUrg]   = { @red@,     @bg@, @red@ },+	[SchemeOcc]   = { @fg@,      @bg@, @surface@ },+	[SchemeEmpty] = { @surface@, @bg@, @surface@ },+	[SchemeKb]    = { @teal@,    @bg@, @surface@ },+};++static const char *kblayouts[] = { "en", "th" };  /* rules */ static const Rule rules[] = {@@ -72,7 +92,9 @@ static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TA #define CHVT(n) { MODCTRL|MODALT, XKB_KEY_F##n, chvt, {.ui = (n)} }  static const char *termcmd[] = { "foot", NULL };-static const char *menucmd[] = { "wmenu-run", NULL };+static const char *menucmd[] = { "wmenu-run", "-f", "Fantasque Sans Mono 11",+	"-N", "@bgRgb@", "-n", "@fgRgb@", "-M", "@bgRgb@", "-m", "@greenRgb@",+	"-S", "@surfaceRgb@", "-s", "@fgRgb@", NULL };  static const Key keys[] = { 	{ MODKEY, XKB_KEY_d, spawn, {.v = menucmd} },@@ -91,6 +113,8 @@ static const Key keys[] = { 	{ MODKEY|MODSHIFT, XKB_KEY_m, setlayout, {.v = &layouts[2]} }, 	{ MODKEY, XKB_KEY_space, togglefloating, {0} }, 	{ MODKEY, XKB_KEY_f, togglefullscreen, {0} },+	{ MODKEY, XKB_KEY_g, togglegaps, {0} },+	{ MODKEY, XKB_KEY_b, togglebar, {0} }, 	{ MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} }, 	{ MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} }, 	{ MODKEY|MODSHIFT, XKB_KEY_comma, tagmon, {.i = WLR_DIRECTION_LEFT} },@@ -113,9 +137,15 @@ static const Key keys[] = { };  static const Button buttons[] = {-	{ MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} },-	{ MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} },-	{ MODKEY, BTN_MIDDLE, togglefloating, {0} },+	{ ClkClient, MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} },+	{ ClkClient, MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} },+	{ ClkClient, MODKEY, BTN_MIDDLE, togglefloating, {0} },+	{ ClkTagBar, 0, BTN_LEFT, view, {0} },+	{ ClkTagBar, 0, BTN_RIGHT, toggleview, {0} },+	{ ClkTagBar, MODKEY, BTN_LEFT, tag, {0} },+	{ ClkTagBar, MODKEY, BTN_RIGHT, toggletag, {0} },+	{ ClkLtSymbol, 0, BTN_LEFT, setlayout, {.v = &layouts[0]} },+	{ ClkLtSymbol, 0, BTN_RIGHT, setlayout, {.v = &layouts[2]} }, };  static const Axis axes[] = {

overlays/dwl/gaps.patch

@@ -0,0 +1,125 @@+From 4ebd22f1a34ee89955acc6bdf930f6676bb6b35d Mon Sep 17 00:00:00 2001+From: peesock <[email protected]>+Date: Mon, 24 Jun 2024 20:06:42 -0700+Subject: [PATCH] gaps!++---+ config.def.h |  4 +++++ dwl.c        | 34 ++++++++++++++++++++++++++--------+ 2 files changed, 30 insertions(+), 8 deletions(-)++diff --git a/config.def.h b/config.def.h+index 1fc6203..b528ca3 100644+--- a/config.def.h++++ b/config.def.h+@@ -6,6 +6,9 @@+ /* appearance */+ 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 int smartgaps                 = 0;  /* 1 means no outer gap when there is only one window */++static int gaps                            = 1;  /* 1 means gaps between windows are added */++static const unsigned int gappx            = 10; /* gap pixel between windows */+ static const unsigned int borderpx         = 1;  /* border pixel of windows */+ static const unsigned int snap             = 32; /* snap pixel */+ static const int showbar                   = 1; /* 0 means no bar */+@@ -139,6 +142,7 @@ static const Key keys[] = {+ 	{ MODKEY,                    XKB_KEY_Return,      zoom,             {0} },+ 	{ MODKEY,                    XKB_KEY_Tab,         view,             {0} },+ 	{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_c,           killclient,       {0} },++	{ MODKEY,                    XKB_KEY_g,           togglegaps,       {0} },+ 	{ MODKEY,                    XKB_KEY_t,           setlayout,        {.v = &layouts[0]} },+ 	{ MODKEY,                    XKB_KEY_f,           setlayout,        {.v = &layouts[1]} },+ 	{ MODKEY,                    XKB_KEY_m,           setlayout,        {.v = &layouts[2]} },+diff --git a/dwl.c b/dwl.c+index 751338b..a348dec 100644+--- a/dwl.c++++ b/dwl.c+@@ -239,6 +239,7 @@ struct Monitor {+ 	struct wlr_box w; /* window area, layout-relative */+ 	struct wl_list layers[4]; /* LayerSurface.link */+ 	const Layout *lt[2];++	int gaps;+ 	unsigned int seltags;+ 	unsigned int sellt;+ 	uint32_t tagset[2];+@@ -392,6 +393,7 @@ 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 togglegaps(const Arg *arg);+ static void toggletag(const Arg *arg);+ static void toggleview(const Arg *arg);+ static void unlocksession(struct wl_listener *listener, void *data);+@@ -1316,6 +1318,8 @@ createmon(struct wl_listener *listener, void *data)+ + 	wlr_output_state_init(&state);+ 	/* Initialize monitor state using configured rules */++	m->gaps = gaps;+++ 	m->tagset[0] = m->tagset[1] = 1;+ 	for (r = monrules; r < END(monrules); r++) {+ 		if (!r->name || strstr(wlr_output->name, r->name)) {+@@ -3110,7 +3114,7 @@ tagmon(const Arg *arg)+ void+ tile(Monitor *m)+ {+-	unsigned int mw, my, ty;++	unsigned int h, r, e = m->gaps, mw, my, ty;+ 	int i, n = 0;+ 	Client *c;+ +@@ -3119,23 +3123,30 @@ tile(Monitor *m)+ 			n++;+ 	if (n == 0)+ 		return;++	if (smartgaps == n)++		e = 0;+ + 	if (n > m->nmaster)+-		mw = m->nmaster ? (int)roundf(m->w.width * m->mfact) : 0;++		mw = m->nmaster ? (int)roundf((m->w.width + gappx*e) * m->mfact) : 0;+ 	else+ 		mw = m->w.width;+-	i = my = ty = 0;++	i = 0;++	my = ty = gappx*e;+ 	wl_list_for_each(c, &clients, link) {+ 		if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)+ 			continue;+ 		if (i < m->nmaster) {+-			resize(c, (struct wlr_box){.x = m->w.x, .y = m->w.y + my, .width = mw,+-				.height = (m->w.height - my) / (MIN(n, m->nmaster) - i)}, 0);+-			my += c->geom.height;++			r = MIN(n, m->nmaster) - i;++			h = (m->w.height - my - gappx*e - gappx*e * (r - 1)) / r;++			resize(c, (struct wlr_box){.x = m->w.x + gappx*e, .y = m->w.y + my,++				.width = mw - 2*gappx*e, .height = h}, 0);++			my += c->geom.height + gappx*e;+ 		} else {++			r = n - i;++			h = (m->w.height - ty - gappx*e - gappx*e * (r - 1)) / r;+ 			resize(c, (struct wlr_box){.x = m->w.x + mw, .y = m->w.y + ty,+-				.width = m->w.width - mw, .height = (m->w.height - ty) / (n - i)}, 0);+-			ty += c->geom.height;++				.width = m->w.width - mw - gappx*e, .height = h}, 0);++			ty += c->geom.height + gappx*e;+ 		}+ 		i++;+ 	}+@@ -3167,6 +3178,13 @@ togglefullscreen(const Arg *arg)+ 		setfullscreen(sel, !sel->isfullscreen);+ }+ ++void++togglegaps(const Arg *arg)++{++	selmon->gaps = !selmon->gaps;++	arrange(selmon);++}+++ void+ toggletag(const Arg *arg)+ {+-- +2.55.0+

overlays/dwl/garden.patch

@@ -0,0 +1,95 @@+diff --git a/config.def.h b/config.def.h+index b528ca3..c41a1e6 100644+--- a/config.def.h++++ b/config.def.h+@@ -22,7 +22,11 @@ static uint32_t colors[][3]                = {+ 	[SchemeNorm] = { 0xbbbbbbff, 0x222222ff, 0x444444ff },+ 	[SchemeSel]  = { 0xeeeeeeff, 0x005577ff, 0x005577ff },+ 	[SchemeUrg]  = { 0,          0,          0x770000ff },++	[SchemeOcc]  = { 0xbbbbbbff, 0x222222ff, 0x444444ff },++	[SchemeEmpty] = { 0x555555ff, 0x222222ff, 0x444444ff },++	[SchemeKb]   = { 0x88aaaaff, 0x222222ff, 0x444444ff },+ };++static const char *kblayouts[] = { "en", "th" };+ + /* tagging */+ static char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };+diff --git a/dwl.c b/dwl.c+index a348dec..474da92 100644+--- a/dwl.c++++ b/dwl.c+@@ -91,7 +91,7 @@+ #define TEXTW(mon, text)        (drwl_font_getwidth(mon->drw, text) + mon->lrpad)+ + /* enums */+-enum { SchemeNorm, SchemeSel, SchemeUrg }; /* color schemes */++enum { SchemeNorm, SchemeSel, SchemeUrg, SchemeOcc, SchemeEmpty, SchemeKb }; /* color schemes */+ enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */+ enum { XDGShell, LayerShell, X11 }; /* client types */+ enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrTop, LyrFS, LyrUnmanaged, LyrOverlay,+@@ -459,6 +459,7 @@ static struct wlr_session_lock_v1 *cur_lock;+ + static struct wlr_seat *seat;+ static KeyboardGroup *kb_group;++static xkb_layout_index_t kblayout;+ static unsigned int cursor_mode;+ static Client *grabc;+ static int grabcx, grabcy; /* client-relative */+@@ -1682,7 +1683,8 @@ 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;++	uint32_t i, occ = 0, urg = 0, sel;++	const char *kb = kblayout < LENGTH(kblayouts) ? kblayouts[kblayout] : "??";+ 	Client *c;+ 	Buffer *buf;+ +@@ -1696,6 +1698,10 @@ drawbar(Monitor *m)+ 		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);++		w = TEXTW(m, kb) + m->lrpad;++		drwl_setscheme(m->drw, colors[SchemeKb]);++		drwl_text(m->drw, m->b.width - tw - w, 0, w, m->b.height, m->lrpad / 2, kb, 0);++		tw += w;+ 	}+ + 	wl_list_for_each(c, &clients, link) {+@@ -1709,12 +1715,11 @@ drawbar(Monitor *m)+ 	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]);++		sel = m->tagset[m->seltags] & 1 << i;++		drwl_setscheme(m->drw, colors[sel ? SchemeSel : occ & 1 << i ? SchemeOcc : SchemeEmpty]);+ 		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);++		if (sel)++			drwl_rect(m->drw, x + m->lrpad / 2, m->b.height - 2, w - m->lrpad, 1, 1, 0);+ 		x += w;+ 	}+ 	w = TEXTW(m, m->ltsymbol);+@@ -1723,7 +1728,7 @@ drawbar(Monitor *m)+ + 	if ((w = m->b.width - tw - x) > m->b.height) {+ 		if (c) {+-			drwl_setscheme(m->drw, colors[m == selmon ? SchemeSel : SchemeNorm]);++			drwl_setscheme(m->drw, colors[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);+@@ -2047,6 +2052,10 @@ keypressmod(struct wl_listener *listener, void *data)+ 		return;+ + 	wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);++	if (group->wlr_group->keyboard.modifiers.group != kblayout) {++		kblayout = group->wlr_group->keyboard.modifiers.group;++		drawbars();++	}+ 	/* Send modifiers to the client. */+ 	wlr_seat_keyboard_notify_modifiers(seat,+ 			&group->wlr_group->keyboard.modifiers);

palette.nix

@@ -0,0 +1,28 @@+{+  colors = {+    bg = "#181b1c";+    bgAlt = "#1f2627";+    surface = "#2f3d3e";+    dim = "#6c8a8a";+    fg = "#d5dcd2";++    red = "#c08a86";+    orange = "#b8b08e";+    yellow = "#cdbf8a";+    green = "#8fae92";+    teal = "#86aaa4";+    blue = "#86a2ac";+    magenta = "#abc3b4";+  };++  ramp = {+    hedge = "#162920";+    slate = "#263e41";+    teal = "#3d575d";+    moss = "#4b684e";+    sage = "#7e9a81";+    lichen = "#98b19e";+    blossom = "#abc3b4";+    cream = "#d5dcd2";+  };+}