personal/dotfiles

patching suckless-tools manually in Nix rather then fetching from personal git (3d56f5233eb7826f336c7a825d5dec7b2a8e5f61)
Repositories | README.md | LICENSE

commit 3d56f5233eb7826f336c7a825d5dec7b2a8e5f61
parent d0aeefadbb9964152a294873cd83ea1a1e0a6024
Author: Friedel Schön <[email protected]>
Date:   Wed, 31 Jul 2024 20:47:40 +0200

patching suckless-tools manually in Nix rather then fetching from personal git

Diffstat:
Aassets/dmenu_path9+++++++++
Mconfigs/dwm.h12++++--------
Mconfigs/st.h8++++----
Mconfigs/surf.h8++++----
Mhome.nix3++-
Apatches/dmenu-dynamicoptions-5.2.diff174+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apatches/dwm-statusbar.diff921+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apatches/slstatus-battery-remaining.diff34++++++++++++++++++++++++++++++++++
Apatches/slstatus-notify.diff144+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apatches/st-remove-terminfo.diff13+++++++++++++
Apatches/st-scrollback-ringbuffer.diff714+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpkgs/dmenu.nix30++++++++++++++++++++++++++----
Mpkgs/dwm.nix26++++++++++++++++++++------
Mpkgs/slstatus.nix13+++++++++----
Mpkgs/st.nix21++++++++++++++++-----
Mpkgs/stw.nix2+-
Mpkgs/surf.nix8++++----
Mpkgs/tabbed.nix8++++----
18 files changed, 2103 insertions(+), 45 deletions(-)

diff --git a/assets/dmenu_path b/assets/dmenu_path @@ -0,0 +1,8 @@ +#!/bin/sh + +# this script prints all binaries found in $PATH +# for some reason does original dmenu_path not work +echo $PATH \ + | xargs -i -d: \ + find {} -maxdepth 1 -type f -executable -printf '%P\n' \ + | sort -u +\ No newline at end of file diff --git a/configs/dwm.h b/configs/dwm.h @@ -33,14 +33,14 @@ static const int systraypinningfailfirst = display systray on the last monitor*/ static const int showsystray = 1; /* 0 means no systray */ static const char *fonts[] = {"Source Code Pro:size=9"}; -static const char *colors[][3] = { // light +static const char *colors_[][3] = { // light /* fg bg border */ [SchemeNorm] = {gray3, white, gray2}, [SchemeSel] = {gray3, blue, blue}, [SchemeUrg] = {gray3, orange, red}, [3] = {gray3, orange, gray2}, [4] = {gray3, green, gray2}}; -static const char *colors_[][3] = { // dark +static const char *colors[][3] = { // dark /* fg bg border */ [SchemeNorm] = {white, gray2, gray2}, [SchemeSel] = {blue, gray3, blue}, @@ -106,11 +106,8 @@ static const Key keys[] = { {MODKEY, XK_Return, spawn, COMMAND("st")}, {MODKEY | ShiftMask, XK_w, spawn, COMMAND("surf")}, {MODKEY, XK_w, spawn, COMMAND("firefox")}, - {MODKEY, XK_space, spawn, - COMMAND("dmenu_run", "-c", "-bh", "5", "-l", "20", "-g", "2")}, - {MODKEY, XK_m, spawn, - SHELL("man -k . | dmenu -c -l 25 | cut -d' ' -f1-2 | sed -E 's/(\\S+) " - "\\((\\S+)\\)/\\2 \\1/' | xargs st -f 'SF Mono' -e man -s")}, + {MODKEY, XK_space, spawn, COMMAND("dmenu_run")}, + {MODKEY, XK_m, spawn, SHELL("man -k | dmenu -l 25 | cut -d' ' -f1-2 | sed -E 's/(\\S+) \\((\\S+)\\)/\\2 \\1/' | xargs st -f 'SF Mono' -e man -s")}, {0, XF86XK_MonBrightnessUp, spawn, BACKLIGHT(monitor_backlight, "+5%")}, {0, XF86XK_MonBrightnessDown, spawn, BACKLIGHT(monitor_backlight, "5%-")}, {0, XF86XK_KbdBrightnessUp, spawn, BACKLIGHT(keyboard_backlight, "+5%")}, @@ -142,7 +139,6 @@ static const Key keys[] = { TAGKEYS(XK_3, 2), TAGKEYS(XK_4, 3), TAGKEYS(XK_5, 4), - {MODKEY | ShiftMask, XK_r, refresh, {0}}, {MODKEY | ShiftMask, XK_q, quit, {0}}, }; diff --git a/configs/st.h b/configs/st.h @@ -146,7 +146,7 @@ static const char **const themes[] = { }}; -static const char *colorname[] = /* themes[GRUVBOX_DARK]; */ { +static const char *colorname__[] = /* themes[GRUVBOX_DARK]; */ { // normal colors "#708089", "#f85552", "#8da101", "#dfa000", "#3a94c5", "#df69ba", "#35a77c", "#939f91", @@ -160,7 +160,7 @@ static const char *colorname[] = /* themes[GRUVBOX_DARK]; */ { }; -static const char *colorname__[] = { // real gruvbox dark +static const char *colorname[] = { // real gruvbox dark "#665c54", "#cc241d", "#98971a", @@ -366,7 +366,7 @@ static Key key[] = { {XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0}, {XK_KP_Delete, ShiftMask, "\033[2K", -1, 0}, {XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0}, - {XK_KP_Delete, XK_ANY_MOD, "\033[3~", -1, 0}, + {XK_KP_Delete, XK_ANY_MOD, "\033[P", -1, 0}, {XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, {XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0}, {XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0}, @@ -434,7 +434,7 @@ static Key key[] = { {XK_Delete, ControlMask, "\033[3;5~", +1, 0}, {XK_Delete, ShiftMask, "\033[2K", -1, 0}, {XK_Delete, ShiftMask, "\033[3;2~", +1, 0}, - {XK_Delete, XK_ANY_MOD, "\033[3~", -1, 0}, + {XK_Delete, XK_ANY_MOD, "\033[p", -1, 0}, {XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, {XK_BackSpace, XK_NO_MOD, "\177", 0, 0}, {XK_BackSpace, Mod1Mask, "\033\177", 0, 0}, diff --git a/configs/surf.h b/configs/surf.h @@ -32,18 +32,18 @@ static Parameter defconfig[ParameterLast] = { [Certificate] = { { .i = 0 }, }, [CaretBrowsing] = { { .i = 0 }, }, [CookiePolicies] = { { .v = "@Aa" }, }, - [DarkMode] = { { .i = 0 }, }, + // [DarkMode] = { { .i = 0 }, }, [DefaultCharset] = { { .v = "UTF-8" }, }, [DiskCache] = { { .i = 1 }, }, [DNSPrefetch] = { { .i = 0 }, }, [Ephemeral] = { { .i = 0 }, }, [FileURLsCrossAccess] = { { .i = 0 }, }, [FontSize] = { { .i = 12 }, }, -// [FrameFlattening] = { { .i = 0 }, }, + [FrameFlattening] = { { .i = 0 }, }, [Geolocation] = { { .i = 0 }, }, [HideBackground] = { { .i = 0 }, }, [Inspector] = { { .i = 0 }, }, -// [Java] = { { .i = 1 }, }, + [Java] = { { .i = 1 }, }, [JavaScript] = { { .i = 1 }, }, [KioskMode] = { { .i = 0 }, }, [LoadImages] = { { .i = 1 }, }, @@ -193,7 +193,7 @@ static Key keys[] = { { MODKEY|GDK_SHIFT_MASK, GDK_KEY_b, toggle, { .i = ScrollBars } }, { MODKEY|GDK_SHIFT_MASK, GDK_KEY_t, toggle, { .i = StrictTLS } }, { MODKEY|GDK_SHIFT_MASK, GDK_KEY_m, toggle, { .i = Style } }, - { MODKEY|GDK_SHIFT_MASK, GDK_KEY_d, toggle, { .i = DarkMode } }, +// { MODKEY|GDK_SHIFT_MASK, GDK_KEY_d, toggle, { .i = DarkMode } }, }; /* button definitions */ diff --git a/home.nix b/home.nix @@ -41,8 +41,9 @@ rec { home.sessionVariables = { EDITOR = "vim"; - PLAN9 = "${pkgs.plan9port}"; + PLAN9 = "${pkgs.plan9port}/plan9"; WEAKBOX = "$HOME/.glibc"; + HOMEMANAGER = ./.; }; nix = { diff --git a/patches/dmenu-dynamicoptions-5.2.diff b/patches/dmenu-dynamicoptions-5.2.diff @@ -0,0 +1,174 @@ +diff --git a/config.def.h b/config.def.h +index 1edb647..035b877 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -7,6 +7,7 @@ static const char *fonts[] = { + "monospace:size=10" + }; + static const char *prompt = NULL; /* -p option; prompt to the left of input field */ ++static const char *dynamic = NULL; /* -dy option; dynamic command to run on input change */ + static const char *colors[SchemeLast][2] = { + /* fg bg */ + [SchemeNorm] = { "#bbbbbb", "#222222" }, +diff --git a/dmenu.1 b/dmenu.1 +index 323f93c..1ae3fe3 100644 +--- a/dmenu.1 ++++ b/dmenu.1 +@@ -22,6 +22,8 @@ dmenu \- dynamic menu + .IR color ] + .RB [ \-w + .IR windowid ] ++.RB [ \-dy ++.IR command ] + .P + .BR dmenu_run " ..." + .SH DESCRIPTION +@@ -80,6 +82,9 @@ prints version information to stdout, then exits. + .TP + .BI \-w " windowid" + embed into windowid. ++.TP ++.BI \-dy " command" ++runs command whenever input changes to update menu items. + .SH USAGE + dmenu is completely controlled by the keyboard. Items are selected using the + arrow keys, page up, page down, home, and end. +diff --git a/dmenu.c b/dmenu.c +index 7cf253b..e7731ae 100644 +--- a/dmenu.c ++++ b/dmenu.c +@@ -44,6 +44,7 @@ static struct item *items = NULL; + static struct item *matches, *matchend; + static struct item *prev, *curr, *next, *sel; + static int mon = -1, screen; ++static unsigned int max_lines = 0; + + static Atom clip, utf8; + static Display *dpy; +@@ -227,6 +228,47 @@ grabkeyboard(void) + die("cannot grab keyboard"); + } + ++static void readstdin(FILE* stream); ++ ++static void ++refreshoptions() ++{ ++ int dynlen = strlen(dynamic); ++ int cmdlen = dynlen + 4; ++ char *cmd; ++ char *c; ++ char *t = text; ++ while (*t) ++ cmdlen += *t++ == '\'' ? 4 : 1; ++ cmd = malloc(cmdlen); ++ if (cmd == NULL) ++ die("cannot malloc %u bytes:", cmdlen); ++ strcpy(cmd, dynamic); ++ t = text; ++ c = cmd + dynlen; ++ *(c++) = ' '; ++ *(c++) = '\''; ++ while (*t) { ++ // prefix ' with '\' ++ if (*t == '\'') { ++ *(c++) = '\''; ++ *(c++) = '\\'; ++ *(c++) = '\''; ++ } ++ *(c++) = *(t++); ++ } ++ *(c++) = '\''; ++ *(c++) = 0; ++ FILE *stream = popen(cmd, "r"); ++ if (!stream) ++ die("could not popen dynamic command (%s):", cmd); ++ readstdin(stream); ++ int r = pclose(stream); ++ if (r == -1) ++ die("could not pclose dynamic command"); ++ free(cmd); ++} ++ + static void + match(void) + { +@@ -238,6 +280,16 @@ match(void) + size_t len, textsize; + struct item *item, *lprefix, *lsubstr, *prefixend, *substrend; + ++ if (dynamic) { ++ refreshoptions(); ++ matches = matchend = NULL; ++ for (item = items; item && item->text; item++) ++ appenditem(item, &matches, &matchend); ++ curr = sel = matches; ++ calcoffsets(); ++ return; ++ } ++ + strcpy(buf, text); + /* separate input text into tokens to be matched individually */ + for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " ")) +@@ -547,14 +599,14 @@ paste(void) + } + + static void +-readstdin(void) ++readstdin(FILE* stream) + { + char *line = NULL; + size_t i, junk, size = 0; + ssize_t len; + + /* read each line from stdin and add it to the item list */ +- for (i = 0; (len = getline(&line, &junk, stdin)) != -1; i++, line = NULL) { ++ for (i = 0; (len = getline(&line, &junk, stream)) != -1; i++, line = NULL) { + if (i + 1 >= size / sizeof *items) + if (!(items = realloc(items, (size += BUFSIZ)))) + die("cannot realloc %zu bytes:", size); +@@ -565,7 +617,7 @@ readstdin(void) + } + if (items) + items[i].text = NULL; +- lines = MIN(lines, i); ++ lines = MIN(max_lines, i); + } + + static void +@@ -711,7 +763,8 @@ static void + usage(void) + { + die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" +- " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]"); ++ " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n" ++ " [-dy command]\n"); + } + + int +@@ -753,6 +806,8 @@ main(int argc, char *argv[]) + colors[SchemeSel][ColFg] = argv[++i]; + else if (!strcmp(argv[i], "-w")) /* embedding window id */ + embed = argv[++i]; ++ else if (!strcmp(argv[i], "-dy")) /* dynamic command to run */ ++ dynamic = argv[++i] && *argv[i] ? argv[i] : NULL; + else + usage(); + +@@ -777,11 +832,14 @@ main(int argc, char *argv[]) + die("pledge"); + #endif + ++ max_lines = lines; + if (fast && !isatty(0)) { + grabkeyboard(); +- readstdin(); ++ if (!dynamic) ++ readstdin(stdin); + } else { +- readstdin(); ++ if (!dynamic) ++ readstdin(stdin); + grabkeyboard(); + } + setup(); diff --git a/patches/dwm-statusbar.diff b/patches/dwm-statusbar.diff @@ -0,0 +1,921 @@ +diff --git a/dwm.1 b/dwm.1 +index ddc8321..53cabd8 100644 +--- a/dwm.1 ++++ b/dwm.1 +@@ -30,6 +30,14 @@ top left corner. The tags which are applied to one or more windows are + indicated with an empty square in the top left corner. + .P + dwm draws a small border around windows to indicate the focus state. ++.P ++On start, dwm can start additional programs that may be specified in two special ++shell scripts (see the FILES section below), autostart_blocking.sh and ++autostart.sh. The former is executed first and dwm will wait for its ++termination before starting. The latter is executed in the background before ++dwm enters its handler loop. ++.P ++Either of these files may be omitted. + .SH OPTIONS + .TP + .B \-v +@@ -92,6 +100,12 @@ Sets monocle layout. + .B Mod1\-space + Toggles between current and previous layout. + .TP ++.B Mod1\-Control\-, ++Cycles backwards in layout list. ++.TP ++.B Mod1\-Control\-. ++Cycles forwards in layout list. ++.TP + .B Mod1\-j + Focus next window. + .TP +@@ -152,6 +166,21 @@ Toggles focused window between floating and tiled state. + .TP + .B Mod1\-Button3 + Resize focused window while dragging. Tiled windows will be toggled to the floating state. ++.SH FILES ++The files containing programs to be started along with dwm are searched for in ++the following directories: ++.IP "1. $XDG_DATA_HOME/dwm" ++.IP "2. $HOME/.local/share/dwm" ++.IP "3. $HOME/.dwm" ++.P ++The first existing directory is scanned for any of the autostart files below. ++.TP 15 ++autostart.sh ++This file is started as a shell background process before dwm enters its handler ++loop. ++.TP 15 ++autostart_blocking.sh ++This file is started before any autostart.sh; dwm waits for its termination. + .SH CUSTOMIZATION + dwm is customized by creating a custom config.h and (re)compiling the source + code. This keeps it fast, secure and simple. +diff --git a/dwm.c b/dwm.c +index f1d86b2..07d2672 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -29,6 +29,7 @@ + #include <string.h> + #include <unistd.h> + #include <sys/types.h> ++#include <sys/stat.h> + #include <sys/wait.h> + #include <X11/cursorfont.h> + #include <X11/keysym.h> +@@ -57,12 +58,28 @@ + #define TAGMASK ((1 << LENGTH(tags)) - 1) + #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + ++#define SYSTEM_TRAY_REQUEST_DOCK 0 ++/* XEMBED messages */ ++#define XEMBED_EMBEDDED_NOTIFY 0 ++#define XEMBED_WINDOW_ACTIVATE 1 ++#define XEMBED_FOCUS_IN 4 ++#define XEMBED_MODALITY_ON 10 ++#define XEMBED_MAPPED (1 << 0) ++#define XEMBED_WINDOW_ACTIVATE 1 ++#define XEMBED_WINDOW_DEACTIVATE 2 ++#define VERSION_MAJOR 0 ++#define VERSION_MINOR 0 ++#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR ++ ++ + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, ++ NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ ++enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ +@@ -141,6 +158,12 @@ typedef struct { + int monitor; + } Rule; + ++typedef struct Systray Systray; ++struct Systray { ++ Window win; ++ Client *icons; ++}; ++ + /* function declarations */ + static void applyrules(Client *c); + static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); +@@ -157,6 +180,7 @@ static void configure(Client *c); + static void configurenotify(XEvent *e); + static void configurerequest(XEvent *e); + static Monitor *createmon(void); ++static void cyclelayout(const Arg *arg); + static void destroynotify(XEvent *e); + static void detach(Client *c); + static void detachstack(Client *c); +@@ -172,6 +196,7 @@ static void focusstack(const Arg *arg); + static Atom getatomprop(Client *c, Atom prop); + static int getrootptr(int *x, int *y); + static long getstate(Window w); ++static unsigned int getsystraywidth(); + static int gettextprop(Window w, Atom atom, char *text, unsigned int size); + static void grabbuttons(Client *c, int focused); + static void grabkeys(void); +@@ -189,13 +214,17 @@ static void pop(Client *c); + static void propertynotify(XEvent *e); + static void quit(const Arg *arg); + static Monitor *recttomon(int x, int y, int w, int h); ++static void removesystrayicon(Client *i); + static void resize(Client *c, int x, int y, int w, int h, int interact); ++static void resizebarwin(Monitor *m); + static void resizeclient(Client *c, int x, int y, int w, int h); + static void resizemouse(const Arg *arg); ++static void resizerequest(XEvent *e); + static void restack(Monitor *m); + static void run(void); ++static void runautostart(void); + static void scan(void); +-static int sendevent(Client *c, Atom proto); ++static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4); + static void sendmon(Client *c, Monitor *m); + static void setclientstate(Client *c, long state); + static void setfocus(Client *c); +@@ -206,6 +235,8 @@ static void setup(void); + static void seturgent(Client *c, int urg); + static void showhide(Client *c); + static void spawn(const Arg *arg); ++static int statuswidth(void); ++static Monitor *systraytomon(Monitor *m); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *m); +@@ -223,18 +254,23 @@ static int updategeom(void); + static void updatenumlockmask(void); + static void updatesizehints(Client *c); + static void updatestatus(void); ++static void updatesystray(void); ++static void updatesystrayicongeom(Client *i, int w, int h); ++static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); + static void updatetitle(Client *c); + static void updatewindowtype(Client *c); + static void updatewmhints(Client *c); + static void view(const Arg *arg); + static Client *wintoclient(Window w); + static Monitor *wintomon(Window w); ++static Client *wintosystrayicon(Window w); + static int xerror(Display *dpy, XErrorEvent *ee); + static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + + /* variables */ ++static Systray *systray = NULL; + static const char broken[] = "broken"; + static char stext[256]; + static int screen; +@@ -257,9 +293,10 @@ static void (*handler[LASTEvent]) (XEvent *) = { + [MapRequest] = maprequest, + [MotionNotify] = motionnotify, + [PropertyNotify] = propertynotify, ++ [ResizeRequest] = resizerequest, + [UnmapNotify] = unmapnotify + }; +-static Atom wmatom[WMLast], netatom[NetLast]; ++static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; + static int running = 1; + static Cur *cursor[CurLast]; + static Clr **scheme; +@@ -441,7 +478,7 @@ buttonpress(XEvent *e) + arg.ui = 1 << i; + } else if (ev->x < x + TEXTW(selmon->ltsymbol)) + click = ClkLtSymbol; +- else if (ev->x > selmon->ww - (int)TEXTW(stext)) ++ else if (ev->x > selmon->ww - (int)TEXTW(stext) - getsystraywidth()) + click = ClkStatusText; + else + click = ClkWinTitle; +@@ -484,6 +521,13 @@ cleanup(void) + XUngrabKey(dpy, AnyKey, AnyModifier, root); + while (mons) + cleanupmon(mons); ++ ++ if (showsystray) { ++ XUnmapWindow(dpy, systray->win); ++ XDestroyWindow(dpy, systray->win); ++ free(systray); ++ } ++ + for (i = 0; i < CurLast; i++) + drw_cur_free(drw, cursor[i]); + for (i = 0; i < LENGTH(colors); i++) +@@ -515,9 +559,58 @@ cleanupmon(Monitor *mon) + void + clientmessage(XEvent *e) + { ++ XWindowAttributes wa; ++ XSetWindowAttributes swa; + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); + ++ if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) { ++ /* add systray icons */ ++ if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { ++ if (!(c = (Client *)calloc(1, sizeof(Client)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Client)); ++ if (!(c->win = cme->data.l[2])) { ++ free(c); ++ return; ++ } ++ c->mon = selmon; ++ c->next = systray->icons; ++ systray->icons = c; ++ if (!XGetWindowAttributes(dpy, c->win, &wa)) { ++ /* use sane defaults */ ++ wa.width = bh; ++ wa.height = bh; ++ wa.border_width = 0; ++ } ++ c->x = c->oldx = c->y = c->oldy = 0; ++ c->w = c->oldw = wa.width; ++ c->h = c->oldh = wa.height; ++ c->oldbw = wa.border_width; ++ c->bw = 0; ++ c->isfloating = True; ++ /* reuse tags field as mapped status */ ++ c->tags = 1; ++ updatesizehints(c); ++ updatesystrayicongeom(c, wa.width, wa.height); ++ XAddToSaveSet(dpy, c->win); ++ XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask); ++ XReparentWindow(dpy, c->win, systray->win, 0, 0); ++ /* use parents background color */ ++ swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; ++ XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa); ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ /* FIXME not sure if I have to send these events, too */ ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ XSync(dpy, False); ++ resizebarwin(selmon); ++ updatesystray(); ++ setclientstate(c, NormalState); ++ } ++ return; ++ } ++ + if (!c) + return; + if (cme->message_type == netatom[NetWMState]) { +@@ -570,7 +663,7 @@ configurenotify(XEvent *e) + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); +- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); ++ resizebarwin(m); + } + focus(NULL); + arrange(NULL); +@@ -647,6 +740,23 @@ createmon(void) + return m; + } + ++void ++cyclelayout(const Arg *arg) { ++ Layout *l; ++ for(l = (Layout *)layouts; l != selmon->lt[selmon->sellt]; l++); ++ if(arg->i > 0) { ++ if(l->symbol && (l + 1)->symbol) ++ setlayout(&((Arg) { .v = (l + 1) })); ++ else ++ setlayout(&((Arg) { .v = layouts })); ++ } else { ++ if(l != layouts && (l - 1)->symbol) ++ setlayout(&((Arg) { .v = (l - 1) })); ++ else ++ setlayout(&((Arg) { .v = &layouts[LENGTH(layouts) - 2] })); ++ } ++} ++ + void + destroynotify(XEvent *e) + { +@@ -655,6 +765,11 @@ destroynotify(XEvent *e) + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); ++ else if ((c = wintosystrayicon(ev->window))) { ++ removesystrayicon(c); ++ resizebarwin(selmon); ++ updatesystray(); ++ } + } + + void +@@ -695,13 +810,33 @@ dirtomon(int dir) + return m; + } + ++int ++statuswidth(void) { ++ char* ts; ++ int tw = 0; ++ ++ tw = TEXTW(stext); ++ for (ts = stext; *ts; ts++) { ++ if ((unsigned int)*ts <= LENGTH(colors)) ++ tw += lrpad; ++ } ++ ++// tw -= lrpad; ++ ++ return tw; ++} ++ + void + drawbar(Monitor *m) + { +- int x, w, tw = 0; ++ int x, w, tw = 0, stw = 0; + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; + unsigned int i, occ = 0, urg = 0; ++ char *ts = stext; ++ char *tp = stext; ++ int tx = 0; ++ char ctmp; + Client *c; + + if (!m->showbar) +@@ -710,8 +845,19 @@ drawbar(Monitor *m) + /* draw status first so it can be overdrawn by tags later */ + if (m == selmon) { /* status is only drawn on selected monitor */ + drw_setscheme(drw, scheme[SchemeNorm]); +- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); ++ tw = statuswidth(); ++ ts = stext; ++ while (1) { ++ if ((unsigned int)*ts > LENGTH(colors)) { ts++; continue ; } ++ ctmp = *ts; ++ *ts = '\0'; ++ drw_text(drw, m->ww - tw + tx - getsystraywidth(), 0, tw - tx, bh, lrpad / 2, tp, 0); ++ tx += TEXTW(tp); ++ if (ctmp == '\0') { break; } ++ drw_setscheme(drw, scheme[(unsigned int)(ctmp-1)]); ++ *ts = ctmp; ++ tp = ++ts; ++ } + } + + for (c = m->clients; c; c = c->next) { +@@ -734,7 +880,7 @@ drawbar(Monitor *m) + drw_setscheme(drw, scheme[SchemeNorm]); + x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); + +- if ((w = m->ww - tw - x) > bh) { ++ if ((w = m->ww - tw - stw - x - getsystraywidth()) > bh) { + if (m->sel) { + drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); +@@ -745,7 +891,7 @@ drawbar(Monitor *m) + drw_rect(drw, x, 0, w, bh, 1, 1); + } + } +- drw_map(drw, m->barwin, 0, 0, m->ww, bh); ++ drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh); + } + + void +@@ -782,8 +928,11 @@ expose(XEvent *e) + Monitor *m; + XExposeEvent *ev = &e->xexpose; + +- if (ev->count == 0 && (m = wintomon(ev->window))) ++ if (ev->count == 0 && (m = wintomon(ev->window))) { + drawbar(m); ++ if (m == selmon) ++ updatesystray(); ++ } + } + + void +@@ -869,14 +1018,32 @@ getatomprop(Client *c, Atom prop) + unsigned char *p = NULL; + Atom da, atom = None; + +- if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, ++ /* FIXME getatomprop should return the number of items and a pointer to ++ * the stored data instead of this workaround */ ++ Atom req = XA_ATOM; ++ if (prop == xatom[XembedInfo]) ++ req = xatom[XembedInfo]; ++ ++ if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, + &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; ++ if (da == xatom[XembedInfo] && dl == 2) ++ atom = ((Atom *)p)[1]; + XFree(p); + } + return atom; + } + ++unsigned int ++getsystraywidth() ++{ ++ unsigned int w = 0; ++ Client *i; ++ if(showsystray) ++ for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ; ++ return w ? w + systrayspacing : 1; ++} ++ + int + getrootptr(int *x, int *y) + { +@@ -1017,7 +1184,8 @@ killclient(const Arg *arg) + { + if (!selmon->sel) + return; +- if (!sendevent(selmon->sel, wmatom[WMDelete])) { ++ ++ if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) { + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XSetCloseDownMode(dpy, DestroyAll); +@@ -1104,6 +1272,13 @@ maprequest(XEvent *e) + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; + ++ Client *i; ++ if ((i = wintosystrayicon(ev->window))) { ++ sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); ++ resizebarwin(selmon); ++ updatesystray(); ++ } ++ + if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect) + return; + if (!wintoclient(ev->window)) +@@ -1225,6 +1400,17 @@ propertynotify(XEvent *e) + Window trans; + XPropertyEvent *ev = &e->xproperty; + ++ if ((c = wintosystrayicon(ev->window))) { ++ if (ev->atom == XA_WM_NORMAL_HINTS) { ++ updatesizehints(c); ++ updatesystrayicongeom(c, c->w, c->h); ++ } ++ else ++ updatesystrayiconstate(c, ev); ++ resizebarwin(selmon); ++ updatesystray(); ++ } ++ + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) + updatestatus(); + else if (ev->state == PropertyDelete) +@@ -1275,6 +1461,19 @@ recttomon(int x, int y, int w, int h) + return r; + } + ++void ++removesystrayicon(Client *i) ++{ ++ Client **ii; ++ ++ if (!showsystray || !i) ++ return; ++ for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next); ++ if (ii) ++ *ii = i->next; ++ free(i); ++} ++ + void + resize(Client *c, int x, int y, int w, int h, int interact) + { +@@ -1282,6 +1481,14 @@ resize(Client *c, int x, int y, int w, int h, int interact) + resizeclient(c, x, y, w, h); + } + ++void ++resizebarwin(Monitor *m) { ++ unsigned int w = m->ww; ++ if (showsystray && m == systraytomon(m) && !systrayonleft) ++ w -= getsystraywidth(); ++ XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh); ++} ++ + void + resizeclient(Client *c, int x, int y, int w, int h) + { +@@ -1297,6 +1504,19 @@ resizeclient(Client *c, int x, int y, int w, int h) + XSync(dpy, False); + } + ++void ++resizerequest(XEvent *e) ++{ ++ XResizeRequestEvent *ev = &e->xresizerequest; ++ Client *i; ++ ++ if ((i = wintosystrayicon(ev->window))) { ++ updatesystrayicongeom(i, ev->width, ev->height); ++ resizebarwin(selmon); ++ updatesystray(); ++ } ++} ++ + void + resizemouse(const Arg *arg) + { +@@ -1390,6 +1610,39 @@ run(void) + handler[ev.type](&ev); /* call handler */ + } + ++void ++runautostart(void) ++{ ++ char rcpath[PATH_MAX]; ++ char* home; ++ pid_t pid; ++ ++ if ((home = getenv("HOME")) == NULL) { ++ home = "/"; ++ } ++ ++ snprintf(rcpath, sizeof(rcpath), "%s/%s", home, dwmrc); ++ if (access(rcpath, X_OK) != 0) ++ return; ++ ++again: ++ if ((pid = fork()) == -1) { ++ fprintf(stderr, "error: unable to fork for autostart, retrying...\n"); ++ sleep(3); ++ goto again; ++ } ++ ++ if (pid == 0) { // child ++ ++ execl(rcpath, rcpath, NULL); ++ _exit(1); ++ } ++ ++ if (waitpid(pid, NULL, 0) == -1) { ++ fprintf(stderr, "warn: unable to wait for autostart, probably still alive\n"); ++ } ++} ++ + void + scan(void) + { +@@ -1443,26 +1696,37 @@ setclientstate(Client *c, long state) + } + + int +-sendevent(Client *c, Atom proto) ++sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) + { + int n; +- Atom *protocols; ++ Atom *protocols, mt; + int exists = 0; + XEvent ev; + +- if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { +- while (!exists && n--) +- exists = protocols[n] == proto; +- XFree(protocols); ++ if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { ++ mt = wmatom[WMProtocols]; ++ if (XGetWMProtocols(dpy, w, &protocols, &n)) { ++ while (!exists && n--) ++ exists = protocols[n] == proto; ++ XFree(protocols); ++ } + } ++ else { ++ exists = True; ++ mt = proto; ++ } ++ + if (exists) { + ev.type = ClientMessage; +- ev.xclient.window = c->win; +- ev.xclient.message_type = wmatom[WMProtocols]; ++ ev.xclient.window = w; ++ ev.xclient.message_type = mt; + ev.xclient.format = 32; +- ev.xclient.data.l[0] = proto; +- ev.xclient.data.l[1] = CurrentTime; +- XSendEvent(dpy, c->win, False, NoEventMask, &ev); ++ ev.xclient.data.l[0] = d0; ++ ev.xclient.data.l[1] = d1; ++ ev.xclient.data.l[2] = d2; ++ ev.xclient.data.l[3] = d3; ++ ev.xclient.data.l[4] = d4; ++ XSendEvent(dpy, w, False, mask, &ev); + } + return exists; + } +@@ -1476,7 +1740,7 @@ setfocus(Client *c) + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); + } +- sendevent(c, wmatom[WMTakeFocus]); ++ sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); + } + + void +@@ -1561,8 +1825,8 @@ setup(void) + drw = drw_create(dpy, screen, root, sw, sh); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); +- lrpad = drw->fonts->h; +- bh = drw->fonts->h + 2; ++ lrpad = drw->fonts->h + horizpadbar; ++ bh = drw->fonts->h + vertpadbar; + updategeom(); + /* init atoms */ + utf8string = XInternAtom(dpy, "UTF8_STRING", False); +@@ -1572,6 +1836,10 @@ setup(void) + wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); ++ netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); ++ netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); ++ netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); ++ netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); + netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); +@@ -1579,6 +1847,9 @@ setup(void) + netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); + netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); ++ xatom[Manager] = XInternAtom(dpy, "MANAGER", False); ++ xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); ++ xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); + /* init cursors */ + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); + cursor[CurResize] = drw_cur_create(drw, XC_sizing); +@@ -1587,6 +1858,8 @@ setup(void) + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); ++ /* init system tray */ ++ updatesystray(); + /* init bars */ + updatebars(); + updatestatus(); +@@ -1649,8 +1922,6 @@ spawn(const Arg *arg) + { + struct sigaction sa; + +- if (arg->v == dmenucmd) +- dmenumon[0] = '0' + selmon->num; + if (fork() == 0) { + if (dpy) + close(ConnectionNumber(dpy)); +@@ -1717,7 +1988,18 @@ togglebar(const Arg *arg) + { + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); +- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++ resizebarwin(selmon); ++ if (showsystray) { ++ XWindowChanges wc; ++ if (!selmon->showbar) ++ wc.y = -bh; ++ else if (selmon->showbar) { ++ wc.y = 0; ++ if (!selmon->topbar) ++ wc.y = selmon->mh - bh; ++ } ++ XConfigureWindow(dpy, systray->win, CWY, &wc); ++ } + arrange(selmon); + } + +@@ -1813,11 +2095,18 @@ unmapnotify(XEvent *e) + else + unmanage(c, 0); + } ++ else if ((c = wintosystrayicon(ev->window))) { ++ /* KLUDGE! sometimes icons occasionally unmap their windows, but do ++ * _not_ destroy them. We map those windows back */ ++ XMapRaised(dpy, c->win); ++ updatesystray(); ++ } + } + + void + updatebars(void) + { ++ unsigned int w; + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, +@@ -1828,10 +2117,15 @@ updatebars(void) + for (m = mons; m; m = m->next) { + if (m->barwin) + continue; +- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), ++ w = m->ww; ++ if (showsystray && m == systraytomon(m)) ++ w -= getsystraywidth(); ++ m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); ++ if (showsystray && m == systraytomon(m)) ++ XMapRaised(dpy, systray->win); + XMapRaised(dpy, m->barwin); + XSetClassHint(dpy, m->barwin, &ch); + } +@@ -1851,7 +2145,7 @@ updatebarpos(Monitor *m) + } + + void +-updateclientlist() ++updateclientlist(void) + { + Client *c; + Monitor *m; +@@ -2008,6 +2302,125 @@ updatestatus(void) + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); + drawbar(selmon); ++ updatesystray(); ++} ++ ++ ++void ++updatesystrayicongeom(Client *i, int w, int h) ++{ ++ if (i) { ++ i->h = bh; ++ if (w == h) ++ i->w = bh; ++ else if (h == bh) ++ i->w = w; ++ else ++ i->w = (int) ((float)bh * ((float)w / (float)h)); ++ applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False); ++ /* force icons into the systray dimensions if they don't want to */ ++ if (i->h > bh) { ++ if (i->w == i->h) ++ i->w = bh; ++ else ++ i->w = (int) ((float)bh * ((float)i->w / (float)i->h)); ++ i->h = bh; ++ } ++ } ++} ++ ++void ++updatesystrayiconstate(Client *i, XPropertyEvent *ev) ++{ ++ long flags; ++ int code = 0; ++ ++ if (!showsystray || !i || ev->atom != xatom[XembedInfo] || ++ !(flags = getatomprop(i, xatom[XembedInfo]))) ++ return; ++ ++ if (flags & XEMBED_MAPPED && !i->tags) { ++ i->tags = 1; ++ code = XEMBED_WINDOW_ACTIVATE; ++ XMapRaised(dpy, i->win); ++ setclientstate(i, NormalState); ++ } ++ else if (!(flags & XEMBED_MAPPED) && i->tags) { ++ i->tags = 0; ++ code = XEMBED_WINDOW_DEACTIVATE; ++ XUnmapWindow(dpy, i->win); ++ setclientstate(i, WithdrawnState); ++ } ++ else ++ return; ++ sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0, ++ systray->win, XEMBED_EMBEDDED_VERSION); ++} ++ ++void ++updatesystray(void) ++{ ++ XSetWindowAttributes wa; ++ XWindowChanges wc; ++ Client *i; ++ Monitor *m = systraytomon(NULL); ++ unsigned int x = m->mx + m->mw; ++ unsigned int sw = TEXTW(stext) - lrpad + systrayspacing; ++ unsigned int w = 1; ++ ++ if (!showsystray) ++ return; ++ if (systrayonleft) ++ x -= sw + lrpad / 2; ++ if (!systray) { ++ /* init systray */ ++ if (!(systray = (Systray *)calloc(1, sizeof(Systray)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); ++ systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel); ++ wa.event_mask = ButtonPressMask | ExposureMask; ++ wa.override_redirect = True; ++ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; ++ XSelectInput(dpy, systray->win, SubstructureNotifyMask); ++ XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32, ++ PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1); ++ XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa); ++ XMapRaised(dpy, systray->win); ++ XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime); ++ if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) { ++ sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0); ++ XSync(dpy, False); ++ } ++ else { ++ fprintf(stderr, "dwm: unable to obtain system tray.\n"); ++ free(systray); ++ systray = NULL; ++ return; ++ } ++ } ++ for (w = 0, i = systray->icons; i; i = i->next) { ++ /* make sure the background color stays the same */ ++ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; ++ XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa); ++ XMapRaised(dpy, i->win); ++ w += systrayspacing; ++ i->x = w; ++ XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h); ++ w += i->w; ++ if (i->mon != m) ++ i->mon = m; ++ } ++ w = w ? w + systrayspacing : 1; ++ x -= w; ++ XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh); ++ wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh; ++ wc.stack_mode = Above; wc.sibling = m->barwin; ++ XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc); ++ XMapWindow(dpy, systray->win); ++ XMapSubwindows(dpy, systray->win); ++ /* redraw background */ ++ XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel); ++ XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh); ++ XSync(dpy, False); + } + + void +@@ -2075,6 +2488,16 @@ wintoclient(Window w) + return NULL; + } + ++Client * ++wintosystrayicon(Window w) { ++ Client *i = NULL; ++ ++ if (!showsystray || !w) ++ return i; ++ for (i = systray->icons; i && i->win != w; i = i->next) ; ++ return i; ++} ++ + Monitor * + wintomon(Window w) + { +@@ -2128,6 +2551,22 @@ xerrorstart(Display *dpy, XErrorEvent *ee) + return -1; + } + ++Monitor * ++systraytomon(Monitor *m) { ++ Monitor *t; ++ int i, n; ++ if(!systraypinning) { ++ if(!m) ++ return selmon; ++ return m == selmon ? m : NULL; ++ } ++ for(n = 1, t = mons; t && t->next; n++, t = t->next) ; ++ for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ; ++ if(systraypinningfailfirst && n < systraypinning) ++ return mons; ++ return t; ++} ++ + void + zoom(const Arg *arg) + { +@@ -2158,6 +2597,7 @@ main(int argc, char *argv[]) + die("pledge"); + #endif /* __OpenBSD__ */ + scan(); ++ runautostart(); + run(); + cleanup(); + XCloseDisplay(dpy); diff --git a/patches/slstatus-battery-remaining.diff b/patches/slstatus-battery-remaining.diff @@ -0,0 +1,34 @@ +diff --git a/components/battery.c b/components/battery.c +index 1c753f9..942e8a1 100644 +--- a/components/battery.c ++++ b/components/battery.c +@@ -111,6 +111,17 @@ + + return ""; + } ++ ++ int battery_discharge(const char *bat) { ++ char path[PATH_MAX], state[12]; ++ ++ if (esnprintf(path, sizeof(path), POWER_SUPPLY_STATUS, bat) < 0) ++ return NULL; ++ if (pscanf(path, "%12[a-zA-Z ]", state) != 1) ++ return NULL; ++ ++ return !strcmp(state, "Discharging"); ++ } + #elif defined(__OpenBSD__) + #include <fcntl.h> + #include <machine/apmvar.h> +diff --git a/slstatus.h b/slstatus.h +index 8ef5874..dacfd98 100644 +--- a/slstatus.h ++++ b/slstatus.h +@@ -4,6 +4,7 @@ + const char *battery_perc(const char *); + const char *battery_remaining(const char *); + const char *battery_state(const char *); ++int battery_discharge(const char *); + + /* cat */ + const char *cat(const char *path); diff --git a/patches/slstatus-notify.diff b/patches/slstatus-notify.diff @@ -0,0 +1,144 @@ +diff --git a/config.def.h b/config.def.h +index d805331..c37c675 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -6,6 +6,9 @@ const unsigned int interval = 1000; + /* text to show if no value can be retrieved */ + static const char unknown_str[] = "n/a"; + ++/* if message is displayed, last max cycles, set to 0 to disable listening */ ++const static int maxmsgcycle = 5; ++ + /* maximum output string length */ + #define MAXLEN 2048 + +diff --git a/slstatus.c b/slstatus.c +index fd31313..b9f8784 100644 +--- a/slstatus.c ++++ b/slstatus.c +@@ -1,10 +1,13 @@ + /* See LICENSE file for copyright and license details. */ + #include <errno.h> ++#include <fcntl.h> + #include <signal.h> + #include <stdio.h> + #include <stdlib.h> + #include <string.h> + #include <time.h> ++#include <unistd.h> ++#include <poll.h> + #include <X11/Xlib.h> + + #include "arg.h" +@@ -13,6 +16,7 @@ + + struct arg { + const char *(*func)(const char *); ++ int (*doenable)(const char *); + const char *fmt; + const char *args; + }; +@@ -21,6 +25,16 @@ char buf[1024]; + static volatile sig_atomic_t done; + static Display *dpy; + ++int always(const char *unused) { ++ (void)unused; ++ return 1; ++} ++ ++int never(const char *unused) { ++ (void)unused; ++ return 0; ++} ++ + #include "config.h" + + static void +@@ -38,10 +52,25 @@ difftimespec(struct timespec *res, struct timespec *a, struct timespec *b) + (a->tv_nsec < b->tv_nsec) * 1E9; + } + ++static int ++getnotify(int fifo, char* buffer, size_t size) ++{ ++ struct pollfd fds = { fifo, POLLIN, 0 }; ++ ++ if (poll(&fds, 1, 0) <= 0) ++ return 0; ++ ++ int len = 0; ++ while (read(fifo, buffer + len, 1) > 0 && buffer[len] != '\n' && len < size) ++ len++; ++ buffer[len] = '\0'; ++ return len; ++} ++ + static void + usage(void) + { +- die("usage: %s [-v] [-s] [-1]", argv0); ++ die("usage: %s [-v] [-s] [-1] [-p fifo]", argv0); + } + + int +@@ -51,8 +80,11 @@ main(int argc, char *argv[]) + struct timespec start, current, diff, intspec, wait; + size_t i, len; + int sflag, ret; ++ int fifo, msgcycle = 0; + char status[MAXLEN]; + const char *res; ++ const char *fifopath = NULL; ++ char *nl; + + sflag = 0; + ARGBEGIN { +@@ -64,6 +96,9 @@ main(int argc, char *argv[]) + case 's': + sflag = 1; + break; ++ case 'p': ++ fifopath = EARGF(usage()); ++ break; + default: + usage(); + } ARGEND +@@ -78,6 +113,9 @@ main(int argc, char *argv[]) + act.sa_flags |= SA_RESTART; + sigaction(SIGUSR1, &act, NULL); + ++ if (fifopath && (fifo = open(fifopath, O_RDONLY)) == -1) ++ die("open(fifo): "); ++ + if (!sflag && !(dpy = XOpenDisplay(NULL))) + die("XOpenDisplay: Failed to open display"); + +@@ -85,8 +123,19 @@ main(int argc, char *argv[]) + if (clock_gettime(CLOCK_MONOTONIC, &start) < 0) + die("clock_gettime:"); + ++ if (msgcycle > 0) { ++ msgcycle--; ++ } else if (fifopath && getnotify(fifo, status, sizeof(status)) > 0) { ++ if (!*status) ++ continue; ++ ++ msgcycle = maxmsgcycle; ++ } else { + status[0] = '\0'; + for (i = len = 0; i < LEN(args); i++) { ++ if (!args[i].doenable(args[i].args)) ++ continue; ++ + if (!(res = args[i].func(args[i].args))) + res = unknown_str; + +@@ -95,6 +144,7 @@ main(int argc, char *argv[]) + break; + + len += ret; ++ } + } + + if (sflag) { diff --git a/patches/st-remove-terminfo.diff b/patches/st-remove-terminfo.diff @@ -0,0 +1,13 @@ +diff --git a/Makefile b/Makefile +index 15db421..e92567a 100644 +--- a/Makefile ++++ b/Makefile +@@ -41,8 +41,6 @@ install: st + mkdir -p $(DESTDIR)$(MANPREFIX)/man1 + sed "s/VERSION/$(VERSION)/g" < st.1 > $(DESTDIR)$(MANPREFIX)/man1/st.1 + chmod 644 $(DESTDIR)$(MANPREFIX)/man1/st.1 +- tic -sx st.info +- @echo Please see the README file regarding the terminfo entry of st. + + uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/st diff --git a/patches/st-scrollback-ringbuffer.diff b/patches/st-scrollback-ringbuffer.diff @@ -0,0 +1,714 @@ +diff --git a/config.def.h b/config.def.h +index 2cd740a..8b25d40 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -201,6 +201,8 @@ static Shortcut shortcuts[] = { + { TERMMOD, XK_Y, selpaste, {.i = 0} }, + { ShiftMask, XK_Insert, selpaste, {.i = 0} }, + { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, ++ { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} }, ++ { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} }, + }; + + /* +diff --git a/st.c b/st.c +index 57c6e96..a3b3c9d 100644 +--- a/st.c ++++ b/st.c +@@ -43,6 +43,10 @@ + #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) + #define ISDELIM(u) (u && wcschr(worddelimiters, u)) + ++#define TSCREEN term.screen[IS_SET(MODE_ALTSCREEN)] ++#define TLINEOFFSET(y) (((y) + TSCREEN.cur - TSCREEN.off + TSCREEN.size) % TSCREEN.size) ++#define TLINE(y) (TSCREEN.buffer[TLINEOFFSET(y)]) ++ + enum term_mode { + MODE_WRAP = 1 << 0, + MODE_INSERT = 1 << 1, +@@ -109,12 +113,21 @@ typedef struct { + int alt; + } Selection; + ++/* Screen lines */ ++typedef struct { ++ Line* buffer; /* ring buffer */ ++ int size; /* size of buffer */ ++ int cur; /* start of active screen */ ++ int off; /* scrollback line offset */ ++ TCursor sc; /* saved cursor */ ++} LineBuffer; ++ + /* Internal representation of the screen */ + typedef struct { + int row; /* nb row */ + int col; /* nb col */ +- Line *line; /* screen */ +- Line *alt; /* alternate screen */ ++ LineBuffer screen[2]; /* screen and alternate screen */ ++ int linelen; /* allocated line length */ + int *dirty; /* dirtyness of lines */ + TCursor c; /* cursor */ + int ocx; /* old cursor col */ +@@ -203,6 +216,8 @@ static void tdeftran(char); + static void tstrsequence(uchar); + + static void drawregion(int, int, int, int); ++static void clearline(Line, Glyph, int, int); ++static Line ensureline(Line); + + static void selnormalize(void); + static void selscroll(int, int); +@@ -408,11 +423,12 @@ int + tlinelen(int y) + { + int i = term.col; ++ Line line = TLINE(y); + +- if (term.line[y][i - 1].mode & ATTR_WRAP) ++ if (line[i - 1].mode & ATTR_WRAP) + return i; + +- while (i > 0 && term.line[y][i - 1].u == ' ') ++ while (i > 0 && line[i - 1].u == ' ') + --i; + + return i; +@@ -521,7 +537,7 @@ selsnap(int *x, int *y, int direction) + * Snap around if the word wraps around at the end or + * beginning of a line. + */ +- prevgp = &term.line[*y][*x]; ++ prevgp = &TLINE(*y)[*x]; + prevdelim = ISDELIM(prevgp->u); + for (;;) { + newx = *x + direction; +@@ -536,14 +552,14 @@ selsnap(int *x, int *y, int direction) + yt = *y, xt = *x; + else + yt = newy, xt = newx; +- if (!(term.line[yt][xt].mode & ATTR_WRAP)) ++ if (!(TLINE(yt)[xt].mode & ATTR_WRAP)) + break; + } + + if (newx >= tlinelen(newy)) + break; + +- gp = &term.line[newy][newx]; ++ gp = &TLINE(newy)[newx]; + delim = ISDELIM(gp->u); + if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim + || (delim && gp->u != prevgp->u))) +@@ -564,14 +580,14 @@ selsnap(int *x, int *y, int direction) + *x = (direction < 0) ? 0 : term.col - 1; + if (direction < 0) { + for (; *y > 0; *y += direction) { +- if (!(term.line[*y-1][term.col-1].mode ++ if (!(TLINE(*y-1)[term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } else if (direction > 0) { + for (; *y < term.row-1; *y += direction) { +- if (!(term.line[*y][term.col-1].mode ++ if (!(TLINE(*y)[term.col-1].mode + & ATTR_WRAP)) { + break; + } +@@ -602,13 +618,13 @@ getsel(void) + } + + if (sel.type == SEL_RECTANGULAR) { +- gp = &term.line[y][sel.nb.x]; ++ gp = &TLINE(y)[sel.nb.x]; + lastx = sel.ne.x; + } else { +- gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; ++ gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0]; + lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; + } +- last = &term.line[y][MIN(lastx, linelen-1)]; ++ last = &TLINE(y)[MIN(lastx, linelen-1)]; + while (last >= gp && last->u == ' ') + --last; + +@@ -949,12 +965,15 @@ int + tattrset(int attr) + { + int i, j; ++ int y = TLINEOFFSET(0); + + for (i = 0; i < term.row-1; i++) { ++ Line line = TSCREEN.buffer[y]; + for (j = 0; j < term.col-1; j++) { +- if (term.line[i][j].mode & attr) ++ if (line[j].mode & attr) + return 1; + } ++ y = (y+1) % TSCREEN.size; + } + + return 0; +@@ -976,14 +995,17 @@ void + tsetdirtattr(int attr) + { + int i, j; ++ int y = TLINEOFFSET(0); + + for (i = 0; i < term.row-1; i++) { ++ Line line = TSCREEN.buffer[y]; + for (j = 0; j < term.col-1; j++) { +- if (term.line[i][j].mode & attr) { ++ if (line[j].mode & attr) { + tsetdirt(i, i); + break; + } + } ++ y = (y+1) % TSCREEN.size; + } + } + +@@ -996,27 +1018,19 @@ tfulldirt(void) + void + tcursor(int mode) + { +- static TCursor c[2]; +- int alt = IS_SET(MODE_ALTSCREEN); +- + if (mode == CURSOR_SAVE) { +- c[alt] = term.c; ++ TSCREEN.sc = term.c; + } else if (mode == CURSOR_LOAD) { +- term.c = c[alt]; +- tmoveto(c[alt].x, c[alt].y); ++ term.c = TSCREEN.sc; ++ tmoveto(term.c.x, term.c.y); + } + } + + void + treset(void) + { +- uint i; +- +- term.c = (TCursor){{ +- .mode = ATTR_NULL, +- .fg = defaultfg, +- .bg = defaultbg +- }, .x = 0, .y = 0, .state = CURSOR_DEFAULT}; ++ int i, j; ++ Glyph g = (Glyph){ .fg = defaultfg, .bg = defaultbg}; + + memset(term.tabs, 0, term.col * sizeof(*term.tabs)); + for (i = tabspaces; i < term.col; i += tabspaces) +@@ -1028,17 +1042,37 @@ treset(void) + term.charset = 0; + + for (i = 0; i < 2; i++) { +- tmoveto(0, 0); +- tcursor(CURSOR_SAVE); +- tclearregion(0, 0, term.col-1, term.row-1); +- tswapscreen(); ++ term.screen[i].sc = (TCursor){{ ++ .fg = defaultfg, ++ .bg = defaultbg ++ }}; ++ term.screen[i].cur = 0; ++ term.screen[i].off = 0; ++ for (j = 0; j < term.row; ++j) { ++ if (term.col != term.linelen) ++ term.screen[i].buffer[j] = xrealloc(term.screen[i].buffer[j], term.col * sizeof(Glyph)); ++ clearline(term.screen[i].buffer[j], g, 0, term.col); ++ } ++ for (j = term.row; j < term.screen[i].size; ++j) { ++ free(term.screen[i].buffer[j]); ++ term.screen[i].buffer[j] = NULL; ++ } + } ++ tcursor(CURSOR_LOAD); ++ term.linelen = term.col; ++ tfulldirt(); + } + + void + tnew(int col, int row) + { +- term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } }; ++ int i; ++ term = (Term){}; ++ term.screen[0].buffer = xmalloc(HISTSIZE * sizeof(Line)); ++ term.screen[0].size = HISTSIZE; ++ term.screen[1].buffer = NULL; ++ for (i = 0; i < HISTSIZE; ++i) term.screen[0].buffer[i] = NULL; ++ + tresize(col, row); + treset(); + } +@@ -1046,14 +1080,42 @@ tnew(int col, int row) + void + tswapscreen(void) + { +- Line *tmp = term.line; +- +- term.line = term.alt; +- term.alt = tmp; + term.mode ^= MODE_ALTSCREEN; + tfulldirt(); + } + ++void ++kscrollup(const Arg *a) ++{ ++ int n = a->i; ++ ++ if (IS_SET(MODE_ALTSCREEN)) ++ return; ++ ++ if (n < 0) n = (-n) * term.row; ++ if (n > TSCREEN.size - term.row - TSCREEN.off) n = TSCREEN.size - term.row - TSCREEN.off; ++ while (!TLINE(-n)) --n; ++ TSCREEN.off += n; ++ selscroll(0, n); ++ tfulldirt(); ++} ++ ++void ++kscrolldown(const Arg *a) ++{ ++ ++ int n = a->i; ++ ++ if (IS_SET(MODE_ALTSCREEN)) ++ return; ++ ++ if (n < 0) n = (-n) * term.row; ++ if (n > TSCREEN.off) n = TSCREEN.off; ++ TSCREEN.off -= n; ++ selscroll(0, -n); ++ tfulldirt(); ++} ++ + void + tscrolldown(int orig, int n) + { +@@ -1062,15 +1124,29 @@ tscrolldown(int orig, int n) + + LIMIT(n, 0, term.bot-orig+1); + +- tsetdirt(orig, term.bot-n); +- tclearregion(0, term.bot-n+1, term.col-1, term.bot); ++ /* Ensure that lines are allocated */ ++ for (i = -n; i < 0; i++) { ++ TLINE(i) = ensureline(TLINE(i)); ++ } + +- for (i = term.bot; i >= orig+n; i--) { +- temp = term.line[i]; +- term.line[i] = term.line[i-n]; +- term.line[i-n] = temp; ++ /* Shift non-scrolling areas in ring buffer */ ++ for (i = term.bot+1; i < term.row; i++) { ++ temp = TLINE(i); ++ TLINE(i) = TLINE(i-n); ++ TLINE(i-n) = temp; ++ } ++ for (i = 0; i < orig; i++) { ++ temp = TLINE(i); ++ TLINE(i) = TLINE(i-n); ++ TLINE(i-n) = temp; + } + ++ /* Scroll buffer */ ++ TSCREEN.cur = (TSCREEN.cur + TSCREEN.size - n) % TSCREEN.size; ++ /* Clear lines that have entered the view */ ++ tclearregion(0, orig, term.linelen-1, orig+n-1); ++ /* Redraw portion of the screen that has scrolled */ ++ tsetdirt(orig+n-1, term.bot); + selscroll(orig, n); + } + +@@ -1082,15 +1158,29 @@ tscrollup(int orig, int n) + + LIMIT(n, 0, term.bot-orig+1); + +- tclearregion(0, orig, term.col-1, orig+n-1); +- tsetdirt(orig+n, term.bot); ++ /* Ensure that lines are allocated */ ++ for (i = term.row; i < term.row + n; i++) { ++ TLINE(i) = ensureline(TLINE(i)); ++ } + +- for (i = orig; i <= term.bot-n; i++) { +- temp = term.line[i]; +- term.line[i] = term.line[i+n]; +- term.line[i+n] = temp; ++ /* Shift non-scrolling areas in ring buffer */ ++ for (i = orig-1; i >= 0; i--) { ++ temp = TLINE(i); ++ TLINE(i) = TLINE(i+n); ++ TLINE(i+n) = temp; ++ } ++ for (i = term.row-1; i >term.bot; i--) { ++ temp = TLINE(i); ++ TLINE(i) = TLINE(i+n); ++ TLINE(i+n) = temp; + } + ++ /* Scroll buffer */ ++ TSCREEN.cur = (TSCREEN.cur + n) % TSCREEN.size; ++ /* Clear lines that have entered the view */ ++ tclearregion(0, term.bot-n+1, term.linelen-1, term.bot); ++ /* Redraw portion of the screen that has scrolled */ ++ tsetdirt(orig, term.bot-n+1); + selscroll(orig, -n); + } + +@@ -1197,6 +1287,7 @@ tsetchar(Rune u, const Glyph *attr, int x, int y) + "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */ "│", "≤", "≥" + "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */ };+ Line + }; ++ Line line = TLINE(y); + + /* + * The table is proudly stolen from rxvt. +@@ -1205,25 +1296,25 @@ tsetchar(Rune u, const Glyph *attr, int x, int y) + BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41]) + utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ); + +- if (term.line[y][x].mode & ATTR_WIDE) { ++ if (line[x].mode & ATTR_WIDE) { + if (x+1 < term.col) { +- term.line[y][x+1].u = ' '; +- term.line[y][x+1].mode &= ~ATTR_WDUMMY; ++ line[x+1].u = ' '; ++ line[x+1].mode &= ~ATTR_WDUMMY; + } +- } else if (term.line[y][x].mode & ATTR_WDUMMY) { +- term.line[y][x-1].u = ' '; +- term.line[y][x-1].mode &= ~ATTR_WIDE; ++ } else if (line[x].mode & ATTR_WDUMMY) { ++ line[x-1].u = ' '; ++ line[x-1].mode &= ~ATTR_WIDE; + } + + term.dirty[y] = 1; +- term.line[y][x] = *attr; +- term.line[y][x].u = u; ++ line[x] = *attr; ++ line[x].u = u; + } + + void + tclearregion(int x1, int y1, int x2, int y2) + { +- int x, y, temp; ++ int x, y, L, S, temp; + Glyph *gp; + + if (x1 > x2) +@@ -1231,15 +1322,16 @@ tclearregion(int x1, int y1, int x2, int y2) + if (y1 > y2) + temp = y1, y1 = y2, y2 = temp; + +- LIMIT(x1, 0, term.col-1); +- LIMIT(x2, 0, term.col-1); ++ LIMIT(x1, 0, term.linelen-1); ++ LIMIT(x2, 0, term.linelen-1); + LIMIT(y1, 0, term.row-1); + LIMIT(y2, 0, term.row-1); + ++ L = TLINEOFFSET(y1); + for (y = y1; y <= y2; y++) { + term.dirty[y] = 1; + for (x = x1; x <= x2; x++) { +- gp = &term.line[y][x]; ++ gp = &TSCREEN.buffer[L][x]; + if (selected(x, y)) + selclear(); + gp->fg = term.c.attr.fg; +@@ -1247,6 +1339,7 @@ tclearregion(int x1, int y1, int x2, int y2) + gp->mode = 0; + gp->u = ' '; + } ++ L = (L + 1) % TSCREEN.size; + } + } + +@@ -1261,7 +1354,7 @@ tdeletechar(int n) + dst = term.c.x; + src = term.c.x + n; + size = term.col - src; +- line = term.line[term.c.y]; ++ line = TLINE(term.c.y); + + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); +@@ -1278,7 +1371,7 @@ tinsertblank(int n) + dst = term.c.x + n; + src = term.c.x; + size = term.col - dst; +- line = term.line[term.c.y]; ++ line = TLINE(term.c.y); + + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(src, term.c.y, dst - 1, term.c.y); +@@ -2082,7 +2175,7 @@ tdumpline(int n) + char buf[UTF_SIZ]; + const Glyph *bp, *end; + +- bp = &term.line[n][0]; ++ bp = &TLINE(n)[0]; + end = &bp[MIN(tlinelen(n), term.col) - 1]; + if (bp != end || bp->u != ' ') { + for ( ; bp <= end; ++bp) +@@ -2469,11 +2562,11 @@ check_control_code: + if (selected(term.c.x, term.c.y)) + selclear(); + +- gp = &term.line[term.c.y][term.c.x]; ++ gp = &TLINE(term.c.y)[term.c.x]; + if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) { + gp->mode |= ATTR_WRAP; + tnewline(1); +- gp = &term.line[term.c.y][term.c.x]; ++ gp = &TLINE(term.c.y)[term.c.x]; + } + + if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) { +@@ -2486,7 +2579,7 @@ check_control_code: + tnewline(1); + else + tmoveto(term.col - width, term.c.y); +- gp = &term.line[term.c.y][term.c.x]; ++ gp = &TLINE(term.c.y)[term.c.x]; + } + + tsetchar(u, &term.c.attr, term.c.x, term.c.y); +@@ -2517,6 +2610,11 @@ twrite(const char *buf, int buflen, int show_ctrl) + Rune u; + int n; + ++ if (TSCREEN.off) { ++ TSCREEN.off = 0; ++ tfulldirt(); ++ } ++ + for (n = 0; n < buflen; n += charsize) { + if (IS_SET(MODE_UTF8)) { + /* process a complete utf8 char */ +@@ -2543,56 +2641,85 @@ twrite(const char *buf, int buflen, int show_ctrl) + } + + void +-tresize(int col, int row) ++clearline(Line line, Glyph g, int x, int xend) + { + int i; ++ g.mode = 0; ++ g.u = ' '; ++ for (i = x; i < xend; ++i) { ++ line[i] = g; ++ } ++} ++ ++Line ++ensureline(Line line) ++{ ++ if (!line) { ++ line = xmalloc(term.linelen * sizeof(Glyph)); ++ } ++ return line; ++} ++ ++void ++tresize(int col, int row) ++{ ++ int i, j; + int minrow = MIN(row, term.row); + int mincol = MIN(col, term.col); ++ int linelen = MAX(col, term.linelen); + int *bp; +- TCursor c; + +- if (col < 1 || row < 1) { ++ if (col < 1 || row < 1 || row > HISTSIZE) { + fprintf(stderr, + "tresize: error resizing to %dx%d\n", col, row); + return; + } + +- /* +- * slide screen to keep cursor where we expect it - +- * tscrollup would work here, but we can optimize to +- * memmove because we're freeing the earlier lines +- */ +- for (i = 0; i <= term.c.y - row; i++) { +- free(term.line[i]); +- free(term.alt[i]); ++ /* Shift buffer to keep the cursor where we expect it */ ++ if (row <= term.c.y) { ++ term.screen[0].cur = (term.screen[0].cur - row + term.c.y + 1) % term.screen[0].size; ++ } ++ ++ /* Resize and clear line buffers as needed */ ++ if (linelen > term.linelen) { ++ for (i = 0; i < term.screen[0].size; ++i) { ++ if (term.screen[0].buffer[i]) { ++ term.screen[0].buffer[i] = xrealloc(term.screen[0].buffer[i], linelen * sizeof(Glyph)); ++ clearline(term.screen[0].buffer[i], term.c.attr, term.linelen, linelen); ++ } ++ } ++ for (i = 0; i < minrow; ++i) { ++ term.screen[1].buffer[i] = xrealloc(term.screen[1].buffer[i], linelen * sizeof(Glyph)); ++ clearline(term.screen[1].buffer[i], term.c.attr, term.linelen, linelen); ++ } + } +- /* ensure that both src and dst are not NULL */ +- if (i > 0) { +- memmove(term.line, term.line + i, row * sizeof(Line)); +- memmove(term.alt, term.alt + i, row * sizeof(Line)); ++ /* Allocate all visible lines for regular line buffer */ ++ for (j = term.screen[0].cur, i = 0; i < row; ++i, j = (j + 1) % term.screen[0].size) ++ { ++ if (!term.screen[0].buffer[j]) { ++ term.screen[0].buffer[j] = xmalloc(linelen * sizeof(Glyph)); ++ } ++ if (i >= term.row) { ++ clearline(term.screen[0].buffer[j], term.c.attr, 0, linelen); ++ } + } +- for (i += row; i < term.row; i++) { +- free(term.line[i]); +- free(term.alt[i]); ++ /* Resize alt screen */ ++ term.screen[1].cur = 0; ++ term.screen[1].size = row; ++ for (i = row; i < term.row; ++i) { ++ free(term.screen[1].buffer[i]); ++ } ++ term.screen[1].buffer = xrealloc(term.screen[1].buffer, row * sizeof(Line)); ++ for (i = term.row; i < row; ++i) { ++ term.screen[1].buffer[i] = xmalloc(linelen * sizeof(Glyph)); ++ clearline(term.screen[1].buffer[i], term.c.attr, 0, linelen); + } + + /* resize to new height */ +- term.line = xrealloc(term.line, row * sizeof(Line)); +- term.alt = xrealloc(term.alt, row * sizeof(Line)); + term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); + term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + +- /* resize each row to new width, zero-pad if needed */ +- for (i = 0; i < minrow; i++) { +- term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); +- term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph)); +- } +- +- /* allocate any new rows */ +- for (/* i = minrow */; i < row; i++) { +- term.line[i] = xmalloc(col * sizeof(Glyph)); +- term.alt[i] = xmalloc(col * sizeof(Glyph)); +- } ++ /* fix tabstops */ + if (col > term.col) { + bp = term.tabs + term.col; + +@@ -2602,26 +2729,16 @@ tresize(int col, int row) + for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) + *bp = 1; + } ++ + /* update terminal size */ + term.col = col; + term.row = row; ++ term.linelen = linelen; + /* reset scrolling region */ + tsetscroll(0, row-1); + /* make use of the LIMIT in tmoveto */ + tmoveto(term.c.x, term.c.y); +- /* Clearing both screens (it makes dirty all lines) */ +- c = term.c; +- for (i = 0; i < 2; i++) { +- if (mincol < col && 0 < minrow) { +- tclearregion(mincol, 0, col - 1, minrow - 1); +- } +- if (0 < col && minrow < row) { +- tclearregion(0, minrow, col - 1, row - 1); +- } +- tswapscreen(); +- tcursor(CURSOR_LOAD); +- } +- term.c = c; ++ tfulldirt(); + } + + void +@@ -2633,14 +2750,15 @@ resettitle(void) + void + drawregion(int x1, int y1, int x2, int y2) + { +- int y; ++ int y, L; + ++ L = TLINEOFFSET(y1); + for (y = y1; y < y2; y++) { +- if (!term.dirty[y]) +- continue; +- +- term.dirty[y] = 0; +- xdrawline(term.line[y], x1, y, x2); ++ if (term.dirty[y]) { ++ term.dirty[y] = 0; ++ xdrawline(TSCREEN.buffer[L], x1, y, x2); ++ } ++ L = (L + 1) % TSCREEN.size; + } + } + +@@ -2655,14 +2773,15 @@ draw(void) + /* adjust cursor position */ + LIMIT(term.ocx, 0, term.col-1); + LIMIT(term.ocy, 0, term.row-1); +- if (term.line[term.ocy][term.ocx].mode & ATTR_WDUMMY) ++ if (TLINE(term.ocy)[term.ocx].mode & ATTR_WDUMMY) + term.ocx--; +- if (term.line[term.c.y][cx].mode & ATTR_WDUMMY) ++ if (TLINE(term.c.y)[cx].mode & ATTR_WDUMMY) + cx--; + + drawregion(0, 0, term.col, term.row); +- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], +- term.ocx, term.ocy, term.line[term.ocy][term.ocx]); ++ if (TSCREEN.off == 0) ++ xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx], ++ term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]); + term.ocx = cx; + term.ocy = term.c.y; + xfinishdraw(); +diff --git a/st.h b/st.h +index fd3b0d8..3cea73b 100644 +--- a/st.h ++++ b/st.h +@@ -19,6 +19,7 @@ + + #define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) + #define IS_TRUECOL(x) (1 << 24 & (x)) ++#define HISTSIZE 2000 + + enum glyph_attribute { + ATTR_NULL = 0, +diff --git a/x.c b/x.c +index bd23686..25785a6 100644 +--- a/x.c ++++ b/x.c +@@ -59,6 +59,8 @@ static void zoom(const Arg *); + static void zoomabs(const Arg *); + static void zoomreset(const Arg *); + static void ttysend(const Arg *); ++void kscrollup(const Arg *); ++void kscrolldown(const Arg *); + + /* config.h for applying patches and the configuration. */ + #include "config.h" diff --git a/pkgs/dmenu.nix b/pkgs/dmenu.nix @@ -4,9 +4,9 @@ with pkgs; stdenv.mkDerivation rec { name = "dmenu"; - src = fetchGit { - url = "https://git.friedelschoen.io/suckless/dmenu"; - rev = "e9bea6daddb02bfdff2915ca781d03a4f071c5dd"; + src = fetchurl { + url = "https://dl.suckless.org/tools/dmenu-5.2.tar.gz"; + hash = "sha256-1NTKd7WRQPJyJy21N+BbuRpZFPVoAmUtxX5hp3PUN5I="; }; buildInputs = [ @@ -15,8 +15,30 @@ with pkgs; stdenv.mkDerivation rec { xorg.libXft ]; + patches = [ + ../patches/dmenu-dynamicoptions-5.2.diff + + (pkgs.fetchurl { + url = https://tools.suckless.org/dmenu/patches/bar_height/dmenu-bar-height-5.2.diff; + hash = "sha256-YzPGmjkjHNIy4kxsY5GthitR/jKkUE7Pl8I8C/pcSLo="; + }) + (pkgs.fetchurl { + url = https://tools.suckless.org/dmenu/patches/case-insensitive/dmenu-caseinsensitive-5.0.diff; + hash = "sha256-TH/3HoIxkFJ+zqDuqISjQLmgjHlYlZKnopjrmxOoZ0U="; + }) + (pkgs.fetchurl { + url = https://tools.suckless.org/dmenu/patches/highlight/dmenu-highlight-4.9.diff; + hash = "sha256-T0Y3YbFt/yVc7cTimJ8HZNQ9zKvd/G1XhfyimPaLQWA="; + }) + (pkgs.fetchurl { + url = https://tools.suckless.org/dmenu/patches/numbers/dmenu-numbers-20220512-28fb3e2.diff; + hash = "sha256-dXAmbub13PUDjygoxsK0PNnCPc5yNWOIPtrNLvy8fSw="; + }) + ]; + configurePhase = '' - cp ${configHeader} config.h + # ln -sf ${../assets/dmenu_path} dmenu_path + # ln -sf ${configHeader} config.h ''; buildPhase = '' diff --git a/pkgs/dwm.nix b/pkgs/dwm.nix @@ -2,21 +2,35 @@ , configHeader }: -with pkgs; stdenv.mkDerivation rec { +pkgs.stdenv.mkDerivation rec { name = "dwm"; - src = fetchGit { - url = "https://git.friedelschoen.io/suckless/dwm"; - rev = "2f0245c39087b0dfce6bb6a1c5269936ab2106b8"; + src = pkgs.fetchurl { + url = https://dl.suckless.org/dwm/dwm-6.5.tar.gz; + hash = "sha256-Ideev6ny+5MUGDbCZmy4H0eExp1k5/GyNS+blwuglyk="; }; - buildInputs = [ + buildInputs = with pkgs; [ xorg.libX11 xorg.libXft xorg.libXinerama ]; + patches = [ + # a mix of these patches: statuscolor, statuspadding, systray + ../patches/dwm-statusbar.diff + + (pkgs.fetchurl { + url = https://dwm.suckless.org/patches/activetagindicatorbar/dwm-activetagindicatorbar-6.2.diff; + hash = "sha256-VKqFvR4u+Q6ya+PqaFAuuYfIZb4i3VN2gBTEb564hyA="; + }) + (pkgs.fetchurl { + url = https://dwm.suckless.org/patches/urgentborder/dwm-6.2-urg-border.diff; + hash = "sha256-nPpKIovwTPKdRL6aiWAr6Mt4dXhryvsTw1l00j1QE8w="; + }) + ]; + configurePhase = '' - cp ${configHeader} config.h + ln -sf ${configHeader} config.h ''; buildPhase = '' diff --git a/pkgs/slstatus.nix b/pkgs/slstatus.nix @@ -2,17 +2,22 @@ with pkgs; stdenv.mkDerivation rec { name = "slstatus"; - src = fetchGit { - url = "https://git.friedelschoen.io/suckless/slstatus"; - rev = "02281e5587b82b790533d7c4bbd146c561ee219c"; + src = fetchurl { + url = https://dl.suckless.org/tools/slstatus-1.0.tar.gz; + hash = "sha256-bW0KFsCN2dIRFywwxHIHASZ6P0DNyTjbPzhvaits/1Q="; }; buildInputs = [ xorg.libX11 ]; + patches = [ + ../patches/slstatus-battery-remaining.diff + ../patches/slstatus-notify.diff + ]; + configurePhase = '' - cp ${configHeader} config.h + ln -s ${configHeader} config.h ''; buildPhase = '' diff --git a/pkgs/st.nix b/pkgs/st.nix @@ -1,10 +1,12 @@ { pkgs ? import <nixpkgs> { }, configHeader }: with pkgs; stdenv.mkDerivation rec { - name = "st"; - src = fetchGit { - url = "https://git.friedelschoen.io/suckless/st"; - rev = "b878d53d99a3a7108107f2da3a856a90872ec6fd"; + pname = "st"; + version = "0.9.2"; + + src = pkgs.fetchurl { + url = "https://dl.suckless.org/st/st-${version}.tar.gz"; + hash = "sha256-ayFdT0crIdYjLzDyIRF6d34kvP7miVXd77dCZGf5SUs="; }; nativeBuildInputs = [ @@ -18,8 +20,17 @@ with pkgs; stdenv.mkDerivation rec { xorg.libXft ]; + patches = [ + ../patches/st-remove-terminfo.diff + ../patches/st-scrollback-ringbuffer.diff + (pkgs.fetchurl { + url = https://st.suckless.org/patches/anysize/st-anysize-20220718-baa9357.diff; + hash = "sha256-eO8MEPRb3uaCTtBznG+LaojXqlcj4eT422rQgpxopfo="; + }) + ]; + configurePhase = '' - cp ${configHeader} config.h + ln -sf ${configHeader} config.h ''; buildPhase = '' diff --git a/pkgs/stw.nix b/pkgs/stw.nix @@ -17,7 +17,7 @@ with pkgs; stdenv.mkDerivation { ]; configurePhase = '' - cp ${configHeader} config.h + ln -sf ${configHeader} config.h ''; buildPhase = '' diff --git a/pkgs/surf.nix b/pkgs/surf.nix @@ -2,9 +2,9 @@ with pkgs; stdenv.mkDerivation rec { name = "surf"; - src = fetchGit { - url = "https://git.suckless.org/surf"; - rev = "9ef79bf7106496c736ba613c51d2fd5af9d873a8"; + src = fetchurl { + url = https://dl.suckless.org/surf/surf-2.1.tar.gz; + hash = "sha256-cuWCkguiWmRiA+k8LSMx2H8DA3ooiU1sfpmvAO4EMlc="; }; nativeBuildInputs = [ @@ -43,7 +43,7 @@ with pkgs; stdenv.mkDerivation rec { ]; configurePhase = '' - cp ${configHeader} config.h + ln -sf ${configHeader} config.h ''; buildPhase = '' diff --git a/pkgs/tabbed.nix b/pkgs/tabbed.nix @@ -2,9 +2,9 @@ with pkgs; stdenv.mkDerivation rec { name = "tabbed"; - src = fetchGit { - url = "https://git.suckless.org/tabbed"; - rev = "7215169fbbb1f81c3bad49b847d1e5907f6ab70c"; + src = fetchurl { + url = "https://dl.suckless.org/tools/tabbed-0.8.tar.gz"; + hash = "sha256-lb3/zLBxCDBo0rVVwlJOnHxXybZElNRsaX5njUmgo9c="; }; buildInputs = [ @@ -12,7 +12,7 @@ with pkgs; stdenv.mkDerivation rec { ]; configurePhase = '' - cp ${configHeader} config.h + ln -sf ${configHeader} config.h ''; buildPhase = ''