feat(*): garden theme for peony
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";+ };+}