Refactored dwm

This commit is contained in:
Clay Gomera 2022-07-20 10:51:13 -04:00
parent 0aadcb22e7
commit 57cecd9032
9 changed files with 326 additions and 277 deletions

View file

@ -63,7 +63,8 @@ static const Rule rules[] = {
static const float mfact = 0.50; /* factor of master area size [0.05..0.95] */
static const int nmaster = 1; /* number of clients in master area */
static const int resizehints = 0; /* 1 means respect size hints in tiled resizals */
#include "layouts.c"
static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */
#include "grid.c"
static const Layout layouts[] = {
/* symbol arrange function */
{ "[]=", tile }, /* first entry is default */
@ -77,13 +78,12 @@ static const Layout layouts[] = {
/* |||--- VARIABLES ---||| */
/* key definitions */
#define MODKEY Mod4Mask
#define TAGKEYS(CHAIN,KEY,TAG) \
{ MODKEY, CHAIN, KEY, view, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask, CHAIN, KEY, toggleview, {.ui = 1 << TAG} }, \
{ MODKEY|ShiftMask, CHAIN, KEY, tag, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask|ShiftMask, CHAIN, KEY, toggletag, {.ui = 1 << TAG} },
#define TAGKEYS(KEY,TAG) \
{1, {{MODKEY, KEY}}, view, {.ui = 1 << TAG} }, \
{1, {{MODKEY|ControlMask, KEY}}, toggleview, {.ui = 1 << TAG} }, \
{1, {{MODKEY|ShiftMask, KEY}}, tag, {.ui = 1 << TAG} }, \
{1, {{MODKEY|ControlMask|ShiftMask, KEY}}, toggletag, {.ui = 1 << TAG} },
/* helper for spawning shell commands in the pre dwm-5.0 fashion */
#define CMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
/* dmenu */
static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */
@ -93,160 +93,160 @@ static const char *termcmd[] = { "alacritty", NULL };
/* |||--- KEY BIDINGS ---||| */
static Key keys[] = {
static Keychord keychords[] = {
/* modifier chain key key function argument */
/* Terminal */
{ MODKEY, -1, XK_Return, spawn, {.v = termcmd } },
{1, {{MODKEY, XK_Return}}, spawn, {.v = termcmd } },
/* Top bar toggle */
{ MODKEY, -1, XK_b, togglebar, {0} },
{1, {{MODKEY|ShiftMask, XK_b}}, togglebar, {0} },
/* WINDOW TAG AND LAYOUT MANIPULATION */
/* Tag Bindings */
TAGKEYS( -1, XK_1, 0)
TAGKEYS( -1, XK_2, 1)
TAGKEYS( -1, XK_3, 2)
TAGKEYS( -1, XK_4, 3)
TAGKEYS( -1, XK_5, 4)
TAGKEYS( -1, XK_6, 5)
TAGKEYS( -1, XK_7, 6)
TAGKEYS( -1, XK_8, 7)
TAGKEYS( -1, XK_9, 8)
TAGKEYS( XK_1, 0)
TAGKEYS( XK_2, 1)
TAGKEYS( XK_3, 2)
TAGKEYS( XK_4, 3)
TAGKEYS( XK_5, 4)
TAGKEYS( XK_6, 5)
TAGKEYS( XK_7, 6)
TAGKEYS( XK_8, 7)
TAGKEYS( XK_9, 8)
/* Close Window */
{ MODKEY, -1, XK_q, killclient, {0} },
{1, {{MODKEY|ShiftMask, XK_c}}, killclient, {0} },
/* Cycle between tags */
{ MODKEY|ControlMask, -1, XK_Tab, view, {0} },
{1, {{MODKEY|ControlMask, XK_Tab}}, view, {0} },
/* Window moving */
{ MODKEY|ShiftMask, -1, XK_j, rotatestack, {.i = +1 } },
{ MODKEY|ShiftMask, -1, XK_k, rotatestack, {.i = -1 } },
{1, {{MODKEY|ShiftMask, XK_j}}, rotatestack, {.i = +1 } },
{1, {{MODKEY|ShiftMask, XK_k}}, rotatestack, {.i = -1 } },
/* Window focusing */
{ MODKEY, -1, XK_j, focusstack, {.i = +1 } },
{ MODKEY, -1, XK_k, focusstack, {.i = -1 } },
{1, {{MODKEY, XK_j}}, focusstack, {.i = +1 } },
{1, {{MODKEY, XK_k}}, focusstack, {.i = -1 } },
/* Increase and decrease master windows count */
{ MODKEY, -1, XK_i, incnmaster, {.i = +1 } },
{ MODKEY, -1, XK_d, incnmaster, {.i = -1 } },
{1, {{MODKEY, XK_i}}, incnmaster, {.i = +1 } },
{1, {{MODKEY, XK_d}}, incnmaster, {.i = -1 } },
/* Increase and decrease master window size */
{ MODKEY, -1, XK_h, setmfact, {.f = -0.05} },
{ MODKEY, -1, XK_l, setmfact, {.f = +0.05} },
{1, {{MODKEY, XK_h}}, setmfact, {.f = -0.05} },
{1, {{MODKEY, XK_l}}, setmfact, {.f = +0.05} },
/* Move window to master */
{ MODKEY|ControlMask, -1, XK_Return, zoom, {0} },
{1, {{MODKEY|ControlMask, XK_Return}}, zoom, {0} },
/* Cycle between layouts fowards and backwards */
{ MODKEY, -1, XK_Tab, cyclelayout, {.i = -1 } },
{ MODKEY|ShiftMask, -1, XK_Tab, cyclelayout, {.i = +1 } },
/* Cycle between recently used layouts */
{ MODKEY, -1, XK_space, setlayout, {0} },
{1, {{ MODKEY, XK_Tab}}, cyclelayout, {.i = -1 } },
{1, {{ MODKEY|ShiftMask, XK_Tab}}, cyclelayout, {.i = +1 } },
/* Switch to tiling layout */
{ MODKEY, -1, XK_t, setlayout, {.v = &layouts[0]} },
{1, {{MODKEY, XK_t}}, setlayout, {.v = &layouts[0]} },
/* Switch to floating layout */
{ MODKEY, -1, XK_f, setlayout, {.v = &layouts[1]} },
{1, {{MODKEY, XK_f}}, setlayout, {.v = &layouts[1]} },
/* Switch to monocle layout */
{ MODKEY, -1, XK_m, setlayout, {.v = &layouts[2]} },
{1, {{MODKEY, XK_m}}, setlayout, {.v = &layouts[2]} },
/* Switch to grid layout */
{ MODKEY, -1, XK_g, setlayout, {.v = &layouts[3]} },
{1, {{MODKEY, XK_g}}, setlayout, {.v = &layouts[3]} },
/* Toggle floating mode */
{ MODKEY|ShiftMask, -1, XK_space, togglefloating, {0} },
{1, {{MODKEY|ShiftMask, XK_f}}, togglefloating, {0} },
/* Toggle fullscreen mode */
{1, {{MODKEY, XK_space}}, togglefullscr, {0} },
/* View all windows of all tags in the current tag */
{ MODKEY, -1, XK_0, view, {.ui = ~0 } },
{1, {{MODKEY, XK_0}}, view, {.ui = ~0 } },
/* Show focused window on all tags */
{ MODKEY|ShiftMask, -1, XK_0, tag, {.ui = ~0 } },
{1, {{MODKEY|ShiftMask, XK_0}}, tag, {.ui = ~0 } },
/* Focusing between monitors */
{ MODKEY, -1, XK_comma, focusmon, {.i = -1 } },
{ MODKEY, -1, XK_period, focusmon, {.i = +1 } },
{1, {{MODKEY, XK_comma}}, focusmon, {.i = -1 } },
{1, {{MODKEY, XK_period}}, focusmon, {.i = +1 } },
/* Move focused window between monitors */
{ MODKEY|ShiftMask, -1, XK_comma, tagmon, {.i = -1 } },
{ MODKEY|ShiftMask, -1, XK_period, tagmon, {.i = +1 } },
{1, {{MODKEY|ShiftMask, XK_comma}}, tagmon, {.i = -1 } },
{1, {{MODKEY|ShiftMask, XK_period}}, tagmon, {.i = +1 } },
/* VOLUME CONTROL */
/* Toggle mute */
{ MODKEY, -1, XK_F1, spawn, CMD("pamixer -t") },
{1, {{MODKEY, XK_F1}}, spawn, SHCMD("pamixer -t") },
/* Decrease volume by 5% */
{ MODKEY, -1, XK_F2, spawn, CMD("pamixer -d 5") },
{1, {{MODKEY, XK_F2}}, spawn, SHCMD("pamixer -d 5") },
/* Increase volume by 5% */
{ MODKEY, -1, XK_F3, spawn, CMD("pamixer -i 5") },
{1, {{MODKEY, XK_F3}}, spawn, SHCMD("pamixer -i 5") },
/* Toggle microphone mute */
{ MODKEY, -1, XK_F4, spawn, CMD("pamixer --default-source -t") },
{1, {{MODKEY, XK_F4}}, spawn, SHCMD("pamixer --default-source -t") },
/* BRIGHTNESS CONTROL */
/* Decrease brightness by 5% */
{ MODKEY, -1, XK_F5, spawn, CMD("xbacklight -dec 10") },
{1, {{MODKEY, XK_F5}}, spawn, SHCMD("xbacklight -dec 10") },
/* Increase brightness by 5% */
{ MODKEY, -1, XK_F6, spawn, CMD("xbacklight -inc 10") },
{1, {{MODKEY, XK_F6}}, spawn, SHCMD("xbacklight -inc 10") },
/* Set screen backlight to off */
{ MODKEY, -1, XK_F7, spawn, CMD("xbacklight -set 0") },
{1, {{MODKEY, XK_F7}}, spawn, SHCMD("xbacklight -set 0") },
/* KEYBOARD LAYOUTS changed with emacs-style keychords SUPER + k (keyboard) followed by "key" */
/* Switch to the spanish keyboard layout */
{ MODKEY, XK_k, XK_e, spawn, CMD("setxkbmap -layout es") },
{2, {{MODKEY, XK_k}, {0, XK_e}}, spawn, SHCMD("setxkbmap -layout es") },
/* Switch to the english keyboard layout */
{ MODKEY, XK_k, XK_u, spawn, CMD("setxkbmap -layout us") },
{2, {{MODKEY, XK_k}, {0, XK_u}}, spawn, SHCMD("setxkbmap -layout us") },
/* EMACS PROGRAMS launched with emacs-style heychords SUPER + e (app) followed by "key" */
{ MODKEY, XK_e, XK_e, spawn, CMD("emacsclient -c -a 'emacs'") },
{ MODKEY, XK_e, XK_b, spawn, CMD("emacsclient -c -a 'emacs' --eval '(ibuffer)'") },
{ MODKEY, XK_e, XK_d, spawn, CMD("emacsclient -c -a 'emacs' --eval '(dired nil)'") },
{ MODKEY, XK_e, XK_t, spawn, CMD("emacsclient -c -a 'emacs' --eval '(+vterm/here nil)'") },
{ MODKEY, XK_e, XK_w, spawn, CMD("emacsclient -c -a 'emacs' --eval '(doom/window-maximize-buffer(eww \"gnu.org\"))'") },
{ MODKEY, XK_e, XK_s, spawn, CMD("emacsclient -c -a 'emacs' --eval '(eshell)'") },
{2, {{MODKEY, XK_e}, {0, XK_e}}, spawn, SHCMD("emacsclient -c -a 'emacs'") },
{2, {{MODKEY, XK_e}, {0, XK_b}}, spawn, SHCMD("emacsclient -c -a 'emacs' --eval '(ibuffer)'") },
{2, {{MODKEY, XK_e}, {0, XK_d}}, spawn, SHCMD("emacsclient -c -a 'emacs' --eval '(dired nil)'") },
{2, {{MODKEY, XK_e}, {0, XK_v}}, spawn, SHCMD("emacsclient -c -a 'emacs' --eval '(+vterm/here nil)'") },
{2, {{MODKEY, XK_e}, {0, XK_s}}, spawn, SHCMD("emacsclient -c -a 'emacs' --eval '(eshell)'") },
{2, {{MODKEY, XK_e}, {0, XK_w}}, spawn, SHCMD("emacsclient -c -a 'emacs' --eval '(doom/window-maximize-buffer(eww \"gnu.org\"))'") },
/* PROGRAMS launched with emacs-style keychords SUPER + a (app) followed by "key" */
/* File manager */
{ MODKEY, XK_a, XK_f, spawn, CMD("alacritty -t exp --class exp,exp -e $HOME/.config/vifm/scripts/vifmrun") },
{2, {{MODKEY, XK_a}, {0, XK_f}}, spawn, SHCMD("alacritty -t exp --class exp,exp -e $HOME/.config/vifm/scripts/vifmrun") },
/* Web browser */
{ MODKEY, XK_a, XK_w, spawn, CMD("qutebrowser") },
{2, {{MODKEY, XK_a}, {0, XK_w}}, spawn, SHCMD("qutebrowser") },
/* Chat app */
{ MODKEY, XK_a, XK_c, spawn, CMD("alacritty -t cht --class cht,cht -e gomuks") },
{2, {{MODKEY, XK_a}, {0, XK_c}}, spawn, SHCMD("alacritty -t cht --class cht,cht -e gomuks") },
/* Music player */
{ MODKEY, XK_a, XK_m, spawn, CMD("alacritty -t msc --class msc,msc -e cmus") },
{2, {{MODKEY, XK_a}, {0, XK_m}}, spawn, SHCMD("alacritty -t msc --class msc,msc -e cmus") },
/* Game app */
{ MODKEY, XK_a, XK_g, spawn, CMD("retroarch") },
{2, {{MODKEY, XK_a}, {0, XK_g}}, spawn, SHCMD("retroarch") },
/* Virtual machine manager */
{ MODKEY, XK_a, XK_v, spawn, CMD("virt-manager") },
{2, {{MODKEY, XK_a}, {0, XK_v}}, spawn, SHCMD("virt-manager") },
/* MISC PROGRAMS launched with emacs-style keychords SUPER + m (app) followed by "key" */
/* System monitor btop */
{ MODKEY, XK_s, XK_b, spawn, CMD("alacritty -t misc --class misc,misc -e btop") },
{2, {{MODKEY, XK_s}, {0, XK_b}}, spawn, SHCMD("alacritty -t misc --class misc,misc -e btop") },
/* System monitor htop */
{ MODKEY, XK_s, XK_h, spawn, CMD("alacritty -t misc --class misc,misc -e htop") },
{2, {{MODKEY, XK_s}, {0, XK_h}}, spawn, SHCMD("alacritty -t misc --class misc,misc -e htop") },
/* Pulse mixer */
{ MODKEY, XK_s, XK_p, spawn, CMD("alacritty -t misc --class misc,misc -e pulsemixer") },
{2, {{MODKEY, XK_s}, {0, XK_p}}, spawn, SHCMD("alacritty -t misc --class misc,misc -e pulsemixer") },
/* Alsa mixer */
{ MODKEY, XK_s, XK_m, spawn, CMD("alacritty -t misc --class misc,misc -e alsamixer") },
{2, {{MODKEY, XK_s}, {0, XK_m}}, spawn, SHCMD("alacritty -t misc --class misc,misc -e alsamixer") },
/* Rss reader */
{ MODKEY, XK_s, XK_n, spawn, CMD("alacritty -t misc --class misc,misc -e newsboat") },
{2, {{MODKEY, XK_s}, {0, XK_n}}, spawn, SHCMD("alacritty -t misc --class misc,misc -e newsboat") },
/* Ytfzf */
{ MODKEY, XK_s, XK_y, spawn, CMD("alacritty -t misc --class misc,misc -e ytfzf -flst") },
{2, {{MODKEY, XK_s}, {0, XK_y}}, spawn, SHCMD("alacritty -t misc --class misc,misc -e ytfzf -flst") },
/* Ani-cli */
{ MODKEY, XK_s, XK_a, spawn, CMD("alacritty -t misc --class misc,misc -e ani-cli") },
{2, {{MODKEY, XK_s}, {0, XK_a}}, spawn, SHCMD("alacritty -t misc --class misc,misc -e ani-cli") },
/* Flix-cli */
{ MODKEY, XK_s, XK_f, spawn, CMD("alacritty -t misc --class misc,misc -e flix-cli") },
{2, {{MODKEY, XK_s}, {0, XK_f}}, spawn, SHCMD("alacritty -t misc --class misc,misc -e flix-cli") },
/* Castero */
{ MODKEY, XK_s, XK_c, spawn, CMD("alacritty -t misc --class misc,misc -e castero") },
{2, {{MODKEY, XK_s}, {0, XK_c}}, spawn, SHCMD("alacritty -t misc --class misc,misc -e castero") },
/* DMENU PROMPTS launched with emacs-style keychords SUPER + p (prompt) followed by "key" */
/* dmenu */
{ MODKEY, XK_p, XK_r, spawn, {.v = dmenucmd } },
{2, {{MODKEY, XK_p}, {0, XK_r}}, spawn, {.v = dmenucmd } },
/* dmenu_power */
{ MODKEY, XK_p, XK_q, spawn, CMD("$HOME/.config/suckless/dmenu/scripts/dmenu_power") },
{2, {{MODKEY, XK_p}, {0, XK_q}}, spawn, SHCMD("$HOME/.config/suckless/dmenu/scripts/dmenu_power") },
/* dmenu_wifi */
{ MODKEY, XK_p, XK_i, spawn, CMD("$HOME/.config/suckless/dmenu/scripts/dmenu_wifi") },
{2, {{MODKEY, XK_p}, {0, XK_i}}, spawn, SHCMD("$HOME/.config/suckless/dmenu/scripts/dmenu_wifi") },
/* dmenu_wall */
{ MODKEY, XK_p, XK_w, spawn, CMD("$HOME/.config/suckless/dmenu/scripts/dmenu_wall") },
{2, {{MODKEY, XK_p}, {0, XK_w}}, spawn, SHCMD("$HOME/.config/suckless/dmenu/scripts/dmenu_wall") },
/* dmenu_edit */
{ MODKEY, XK_p, XK_e, spawn, CMD("$HOME/.config/suckless/dmenu/scripts/dmenu_edit") },
{2, {{MODKEY, XK_p}, {0, XK_e}}, spawn, SHCMD("$HOME/.config/suckless/dmenu/scripts/dmenu_edit") },
/* dmenu_scrot */
{ MODKEY, XK_p, XK_s, spawn, CMD("$HOME/.config/suckless/dmenu/scripts/dmenu_scrot") },
{2, {{MODKEY, XK_p}, {0, XK_s}}, spawn, SHCMD("$HOME/.config/suckless/dmenu/scripts/dmenu_scrot") },
/* dmenu_drun */
{ MODKEY, XK_p, XK_d, spawn, CMD("$HOME/.config/suckless/dmenu/scripts/dmenu_drun") },
{2, {{MODKEY, XK_p}, {0, XK_d}}, spawn, SHCMD("$HOME/.config/suckless/dmenu/scripts/dmenu_drun") },
/* dmenu_blue */
{ MODKEY, XK_p, XK_b, spawn, CMD("$HOME/.config/suckless/dmenu/scripts/dmenu_blue") },
{2, {{MODKEY, XK_p}, {0, XK_b}}, spawn, SHCMD("$HOME/.config/suckless/dmenu/scripts/dmenu_blue") },
/* dmenu_emoji */
{ MODKEY, XK_p, XK_z, spawn, CMD("$HOME/.config/suckless/dmenu/scripts/dmenu_emoji") },
{2, {{MODKEY, XK_p}, {0, XK_z}}, spawn, SHCMD("$HOME/.config/suckless/dmenu/scripts/dmenu_emoji") },
/* DWM BOOTSTRAP */
{ MODKEY|ShiftMask|ControlMask, -1, XK_q, quit, {0} },
{ MODKEY|ControlMask, -1, XK_r, quit, {1} },
{1, {{MODKEY|ControlMask, XK_r}}, quit, {1} },
{1, {{MODKEY|ShiftMask, XK_q}}, quit, {0} },
};
/* button definitions */

View file

@ -1,3 +1,4 @@
/* See LICENSE file for copyright and license details. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -97,6 +98,7 @@ drw_free(Drw *drw)
{
XFreePixmap(drw->dpy, drw->drawable);
XFreeGC(drw->dpy, drw->gc);
drw_fontset_free(drw->fonts);
free(drw);
}
@ -134,6 +136,19 @@ xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
die("no font specified.");
}
/* Do not allow using color fonts. This is a workaround for a BadLength
* error from Xft with color glyphs. Modelled on the Xterm workaround. See
* https://bugzilla.redhat.com/show_bug.cgi?id=1498269
* https://lists.suckless.org/dev/1701/30932.html
* https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349
* and lots more all over the internet.
*/
FcBool iscol;
if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) {
XftFontClose(drw->dpy, xfont);
return NULL;
}
font = ecalloc(1, sizeof(Fnt));
font->xfont = xfont;
font->pattern = pattern;
@ -240,12 +255,10 @@ drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int
int
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
{
char buf[1024];
int ty;
unsigned int ew;
int i, ty, ellipsis_x = 0;
unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len;
XftDraw *d = NULL;
Fnt *usedfont, *curfont, *nextfont;
size_t i, len;
int utf8strlen, utf8charlen, render = x || y || w || h;
long utf8codepoint = 0;
const char *utf8str;
@ -253,13 +266,17 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
FcPattern *fcpattern;
FcPattern *match;
XftResult result;
int charexists = 0;
int charexists = 0, overflow = 0;
/* keep track of a couple codepoints for which we have no match. */
enum { nomatches_len = 64 };
static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches;
static unsigned int ellipsis_width = 0;
if (!drw || (render && !drw->scheme) || !text || !drw->fonts)
if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts)
return 0;
if (!render) {
w = ~w;
w = invert ? invert : ~invert;
} else {
XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
@ -269,8 +286,10 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
}
usedfont = drw->fonts;
if (!ellipsis_width && render)
ellipsis_width = drw_fontset_getwidth(drw, "...");
while (1) {
utf8strlen = 0;
ew = ellipsis_len = utf8strlen = 0;
utf8str = text;
nextfont = NULL;
while (*text) {
@ -278,9 +297,27 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
for (curfont = drw->fonts; curfont; curfont = curfont->next) {
charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
if (charexists) {
if (curfont == usedfont) {
drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL);
if (ew + ellipsis_width <= w) {
/* keep track where the ellipsis still fits */
ellipsis_x = x + ew;
ellipsis_w = w - ew;
ellipsis_len = utf8strlen;
}
if (ew + tmpw > w) {
overflow = 1;
/* called from drw_fontset_getwidth_clamp():
* it wants the width AFTER the overflow
*/
if (!render)
x += tmpw;
else
utf8strlen = ellipsis_len;
} else if (curfont == usedfont) {
utf8strlen += utf8charlen;
text += utf8charlen;
ew += tmpw;
} else {
nextfont = curfont;
}
@ -288,36 +325,25 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
}
}
if (!charexists || nextfont)
if (overflow || !charexists || nextfont)
break;
else
charexists = 0;
}
if (utf8strlen) {
drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL);
/* shorten text if necessary */
for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--)
drw_font_getexts(usedfont, utf8str, len, &ew, NULL);
if (len) {
memcpy(buf, utf8str, len);
buf[len] = '\0';
if (len < utf8strlen)
for (i = len; i && i > len - 3; buf[--i] = '.')
; /* NOP */
if (render) {
ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
usedfont->xfont, x, ty, (XftChar8 *)buf, len);
usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen);
}
x += ew;
w -= ew;
}
}
if (render && overflow)
drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert);
if (!*text) {
if (!*text || overflow) {
break;
} else if (nextfont) {
charexists = 0;
@ -327,6 +353,12 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
* character must be drawn. */
charexists = 1;
for (i = 0; i < nomatches_len; ++i) {
/* avoid calling XftFontMatch if we know we won't find a match */
if (utf8codepoint == nomatches.codepoint[i])
goto no_match;
}
fccharset = FcCharSetCreate();
FcCharSetAddChar(fccharset, utf8codepoint);
@ -355,6 +387,8 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
curfont->next = usedfont;
} else {
xfont_free(usedfont);
nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint;
no_match:
usedfont = drw->fonts;
}
}
@ -384,6 +418,15 @@ drw_fontset_getwidth(Drw *drw, const char *text)
return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
}
unsigned int
drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n)
{
unsigned int tmp = 0;
if (drw && drw->fonts && text && n)
tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n);
return MIN(n, tmp);
}
void
drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h)
{
@ -421,4 +464,3 @@ drw_cur_free(Drw *drw, Cur *cursor)
XFreeCursor(drw->dpy, cursor->cursor);
free(cursor);
}

View file

@ -1,8 +1,4 @@
/* ____ __ */
/* / __ \_________ _/ /_____ */
/* / / / / ___/ __ `/ //_/ _ \ */
/* / /_/ / / / /_/ / ,< / __/ Clay Gomera (Drake) */
/* /_____/_/ \__,_/_/|_|\___/ My custom dwm build */
/* See LICENSE file for copyright and license details. */
typedef struct {
Cursor cursor;
@ -42,6 +38,7 @@ void drw_free(Drw *drw);
Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount);
void drw_fontset_free(Fnt* set);
unsigned int drw_fontset_getwidth(Drw *drw, const char *text);
unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n);
void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h);
/* Colorscheme abstraction */
@ -62,4 +59,3 @@ int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned in
/* Map functions */
void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);

View file

@ -30,10 +30,18 @@ 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
prints version information to standard output, then exits.
prints version information to stderr, then exits.
.SH USAGE
.SS Status bar
.TP
@ -122,6 +130,9 @@ Zooms/cycles focused window to/from master area (tiled layouts only).
.B Mod1\-Shift\-c
Close focused window.
.TP
.B Mod1\-Shift\-f
Toggle fullscreen for focused window.
.TP
.B Mod1\-Shift\-space
Toggle focused window between tiled and floating state.
.TP
@ -161,6 +172,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.

View file

@ -1,9 +1,5 @@
/* ____ __ */
/* / __ \_________ _/ /_____ */
/* / / / / ___/ __ `/ //_/ _ \ */
/* / /_/ / / / /_/ / ,< / __/ Clay Gomera (Drake) */
/* /_____/_/ \__,_/_/|_|\___/ My custom dwm build */
/*
/* See LICENSE file for copyright and license details.
*
* dynamic window manager is designed like any other X client as well. It is
* driven through handling X events. In contrast to other X clients, a window
* manager selects for SubstructureRedirectMask on the root window, to receive
@ -24,7 +20,6 @@
*
* To understand everything else, start reading main().
*/
#include <errno.h>
#include <locale.h>
#include <signal.h>
@ -34,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>
@ -54,8 +50,7 @@
#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))
#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \
* MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy)))
#define ISVISIBLEONTAG(C, T) ((C->tags & T))
#define ISVISIBLE(C) ISVISIBLEONTAG(C, C->mon->tagset[C->mon->seltags])
#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]))
#define LENGTH(X) (sizeof X / sizeof X[0])
#define MOUSEMASK (BUTTONMASK|PointerMotionMask)
#define WIDTH(X) ((X)->w + 2 * (X)->bw + gappx)
@ -97,7 +92,7 @@ struct Client {
float mina, maxa;
int x, y, w, h;
int oldx, oldy, oldw, oldh;
int basew, baseh, incw, inch, maxw, maxh, minw, minh;
int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid;
int bw, oldbw;
unsigned int tags;
int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
@ -109,11 +104,15 @@ struct Client {
typedef struct {
unsigned int mod;
KeySym chain;
KeySym keysym;
} Key;
typedef struct {
unsigned int n;
const Key keys[5];
void (*func)(const Arg *);
const Arg arg;
} Key;
} Keychord;
typedef struct {
const char *symbol;
@ -156,7 +155,7 @@ static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interac
static void arrange(Monitor *m);
static void arrangemon(Monitor *m);
static void attach(Client *c);
static void attachaside(Client *c);
static void attachbottom(Client *c);
static void attachstack(Client *c);
static void buttonpress(XEvent *e);
static void checkotherwm(void);
@ -182,6 +181,7 @@ static void focus(Client *c);
static void focusin(XEvent *e);
static void focusmon(const Arg *arg);
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 int gettextprop(Window w, Atom atom, char *text, unsigned int size);
@ -196,7 +196,6 @@ static void maprequest(XEvent *e);
static void monocle(Monitor *m);
static void motionnotify(XEvent *e);
static void movemouse(const Arg *arg);
static Client *nexttagged(Client *c);
static Client *nexttiled(Client *c);
static void pop(Client *);
static void propertynotify(XEvent *e);
@ -229,6 +228,7 @@ static void tagmon(const Arg *arg);
static void tile(Monitor *);
static void togglebar(const Arg *arg);
static void togglefloating(const Arg *arg);
static void togglefullscr(const Arg *arg);
static void toggletag(const Arg *arg);
static void toggleview(const Arg *arg);
static void unfocus(Client *c, int setfocus);
@ -287,7 +287,7 @@ static Display *dpy;
static Drw *drw;
static Monitor *mons, *selmon;
static Window root, wmcheckwin;
static KeySym keychain = -1;
unsigned int currentkey = 0;
static int useargb = 0;
static Visual *visual;
@ -370,6 +370,8 @@ applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact)
if (*w < bh)
*w = bh;
if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) {
if (!c->hintsvalid)
updatesizehints(c);
/* see last two sentences in ICCCM 4.1.2.3 */
baseismin = c->basew == c->minw && c->baseh == c->minh;
if (!baseismin) { /* temporarily remove base dimensions */
@ -433,17 +435,14 @@ attach(Client *c)
}
void
attachaside(Client *c) {
Client *at = nexttagged(c);
if(!at) {
attach(c);
return;
}
c->next = at->next;
at->next = c;
attachbottom(Client *c)
{
Client **tc;
c->next = NULL;
for (tc = &c->mon->clients; *tc; tc = &(*tc)->next);
*tc = c;
}
void
attachstack(Client *c)
{
@ -477,7 +476,7 @@ buttonpress(XEvent *e)
arg.ui = 1 << i;
} else if (ev->x < x + blw)
click = ClkLtSymbol;
else if (ev->x > selmon->ww - TEXTW(stext))
else if (ev->x > selmon->ww - (int)TEXTW(stext))
click = ClkStatusText;
else
click = ClkWinTitle;
@ -524,6 +523,7 @@ cleanup(void)
drw_cur_free(drw, cursor[i]);
for (i = 0; i < LENGTH(colors); i++)
free(scheme[i]);
free(scheme);
XDestroyWindow(dpy, wmcheckwin);
drw_free(drw);
XSync(dpy, False);
@ -750,17 +750,20 @@ dirtomon(int dir)
void
drawbar(Monitor *m)
{
int x, w, sw = 0;
int x, w, tw = 0;
int boxs = drw->fonts->h / 9;
int boxw = drw->fonts->h / 6 + 2;
unsigned int i, occ = 0, urg = 0;
Client *c;
if (!m->showbar)
return;
/* draw status first so it can be overdrawn by tags later */
if (m == selmon || 1) { /* status is only drawn on selected monitor */
if (m == selmon) { /* status is only drawn on selected monitor */
drw_setscheme(drw, scheme[SchemeNorm]);
sw = TEXTW(stext);
drw_text(drw, m->ww - sw, 0, sw, bh, lrpad / 2, stext, 0);
tw = TEXTW(stext);
drw_text(drw, m->ww - tw, 0, tw, bh, lrpad / 2, stext, 0);
}
for (c = m->clients; c; c = c->next) {
@ -783,7 +786,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 - sw - x) > bh) {
if ((w = m->ww - tw - x) > 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);
@ -911,7 +914,7 @@ focusstack(const Arg *arg)
{
Client *c = NULL, *i;
if (!selmon->sel)
if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen))
return;
if (arg->i > 0) {
for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next);
@ -1027,22 +1030,17 @@ grabkeys(void)
{
updatenumlockmask();
{
unsigned int i, j;
unsigned int i, k;
unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
KeyCode code;
KeyCode chain;
XUngrabKey(dpy, AnyKey, AnyModifier, root);
for (i = 0; i < LENGTH(keys); i++)
if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) {
if (keys[i].chain != -1 &&
((chain = XKeysymToKeycode(dpy, keys[i].chain))))
code = chain;
for (j = 0; j < LENGTH(modifiers); j++)
XGrabKey(dpy, code, keys[i].mod | modifiers[j], root,
for (i = 0; i < LENGTH(keychords); i++)
if ((code = XKeysymToKeycode(dpy, keychords[i].keys[currentkey].keysym)))
for (k = 0; k < LENGTH(modifiers); k++)
XGrabKey(dpy, code, keychords[i].keys[currentkey].mod | modifiers[k], root,
True, GrabModeAsync, GrabModeAsync);
}
}
}
void
@ -1067,37 +1065,48 @@ isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info)
void
keypress(XEvent *e)
{
unsigned int i, j;
XEvent event = *e;
Keychord *keychord;
unsigned int ran = 0;
KeySym keysym;
XKeyEvent *ev;
int current = 0;
unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
Keychord *newoptions;
Keychord *oldoptions = (Keychord *)malloc(sizeof(keychords));
ev = &e->xkey;
memcpy(oldoptions, keychords, sizeof(keychords));
size_t numoption = 0;
while(!ran){
ev = &event.xkey;
keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
for (i = 0; i < LENGTH(keys); i++) {
if (keysym == keys[i].keysym && keys[i].chain == -1
&& CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
&& keys[i].func)
keys[i].func(&(keys[i].arg));
else if (keysym == keys[i].chain && keychain == -1
&& CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
&& keys[i].func) {
current = 1;
keychain = keysym;
for (j = 0; j < LENGTH(modifiers); j++)
XGrabKey(dpy, AnyKey, 0 | modifiers[j], root,
True, GrabModeAsync, GrabModeAsync);
} else if (!current && keysym == keys[i].keysym
&& keychain != -1
&& keys[i].chain == keychain
&& keys[i].func)
keys[i].func(&(keys[i].arg));
newoptions = (Keychord *)malloc(0);
numoption = 0;
for (keychord = oldoptions; keychord->n != 0 && currentkey < 5; keychord = (Keychord *)((char *)keychord + sizeof(Keychord))){
if(keysym == keychord->keys[currentkey].keysym
&& CLEANMASK(keychord->keys[currentkey].mod) == CLEANMASK(ev->state)
&& keychord->func){
if(keychord->n == currentkey +1){
keychord->func(&(keychord->arg));
ran = 1;
}else{
numoption++;
newoptions = (Keychord *)realloc(newoptions, numoption * sizeof(Keychord));
memcpy((char *)newoptions + (numoption -1) * sizeof(Keychord),keychord, sizeof(Keychord));
}
if (!current) {
keychain = -1;
}
}
currentkey++;
if(numoption == 0)
break;
grabkeys();
while (running && !XNextEvent(dpy, &event) && !ran)
if(event.type == KeyPress)
break;
free(oldoptions);
oldoptions = newoptions;
}
free(newoptions);
currentkey = 0;
grabkeys();
}
void
@ -1164,7 +1173,7 @@ manage(Window w, XWindowAttributes *wa)
c->isfloating = c->oldstate = trans != None || c->isfixed;
if (c->isfloating)
XRaiseWindow(dpy, c->win);
attachaside(c);
attachbottom(c);
attachstack(c);
XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend,
(unsigned char *) &(c->win), 1);
@ -1294,16 +1303,6 @@ movemouse(const Arg *arg)
}
}
Client *
nexttagged(Client *c) {
Client *walked = c->mon->clients;
for(;
walked && (walked->isfloating || !ISVISIBLEONTAG(walked, c->tags));
walked = walked->next
);
return walked;
}
Client *
nexttiled(Client *c)
{
@ -1340,7 +1339,7 @@ propertynotify(XEvent *e)
arrange(c->mon);
break;
case XA_WM_NORMAL_HINTS:
updatesizehints(c);
c->hintsvalid = 0;
break;
case XA_WM_HINTS:
updatewmhints(c);
@ -1396,15 +1395,15 @@ resizeclient(Client *c, int x, int y, int w, int h)
wc.border_width = c->bw;
/* Get number of clients for the selected monitor */
for (n = 0, nbc = nexttiled(selmon->clients); nbc; nbc = nexttiled(nbc->next), n++);
/* Get number of clients for the client's monitor */
for (n = 0, nbc = nexttiled(c->mon->clients); nbc; nbc = nexttiled(nbc->next), n++);
/* Do nothing if layout is floating */
if (c->isfloating || selmon->lt[selmon->sellt]->arrange == NULL) {
if (c->isfloating || c->mon->lt[c->mon->sellt]->arrange == NULL) {
gapincr = gapoffset = 0;
} else {
/* Remove border and gap if layout is monocle or only one client */
if (selmon->lt[selmon->sellt]->arrange == monocle || n == 1) {
if (c->mon->lt[c->mon->sellt]->arrange == monocle || n == 1) {
gapoffset = 0;
gapincr = -2 * borderpx;
wc.border_width = 0;
@ -1591,7 +1590,7 @@ sendmon(Client *c, Monitor *m)
detachstack(c);
c->mon = m;
c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
attachaside(c);
attachbottom(c);
attachstack(c);
focus(NULL);
arrange(NULL);
@ -1694,7 +1693,7 @@ setmfact(const Arg *arg)
if (!arg || !selmon->lt[selmon->sellt]->arrange)
return;
f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
if (f < 0.1 || f > 0.9)
if (f < 0.05 || f > 0.95)
return;
selmon->mfact = f;
arrange(selmon);
@ -1879,11 +1878,13 @@ tile(Monitor *m)
for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
if (i < m->nmaster) {
h = (m->wh - my) / (MIN(n, m->nmaster) - i);
resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0);
resize(c, m->wx, m->wy + my, mw - (2*c->bw) + (n > 1 ? gappx : 0), h - (2*c->bw), 0);
if (my + HEIGHT(c) < m->wh)
my += HEIGHT(c);
} else {
h = (m->wh - ty) / (n - i);
resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0);
if (ty + HEIGHT(c) < m->wh)
ty += HEIGHT(c);
}
}
@ -1911,6 +1912,13 @@ togglefloating(const Arg *arg)
arrange(selmon);
}
void
togglefullscr(const Arg *arg)
{
if(selmon->sel)
setfullscreen(selmon->sel, !selmon->sel->isfullscreen);
}
void
toggletag(const Arg *arg)
{
@ -2062,8 +2070,9 @@ updategeom(void)
memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo));
XFree(info);
nn = j;
if (n <= nn) { /* new monitors available */
for (i = 0; i < (nn - n); i++) {
/* new monitors if nn > n */
for (i = n; i < nn; i++) {
for (m = mons; m && m->next; m = m->next);
if (m)
m->next = createmon();
@ -2083,7 +2092,7 @@ updategeom(void)
m->mh = m->wh = unique[i].height;
updatebarpos(m);
}
} else { /* less monitors available nn < n */
/* removed monitors if n > nn */
for (i = nn; i < n; i++) {
for (m = mons; m && m->next; m = m->next);
while ((c = m->clients)) {
@ -2092,14 +2101,13 @@ updategeom(void)
detachstack(c);
c->mon = mons;
attach(c);
attachaside(c);
attachbottom(c);
attachstack(c);
}
if (m == selmon)
selmon = mons;
cleanupmon(m);
}
}
free(unique);
} else
#endif /* XINERAMA */
@ -2177,16 +2185,15 @@ updatesizehints(Client *c)
} else
c->maxa = c->mina = 0.0;
c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh);
c->hintsvalid = 1;
}
void
updatestatus(void)
{
Monitor* m;
if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
strcpy(stext, "dwm-"VERSION);
for(m = mons; m; m = m->next)
drawbar(m);
drawbar(selmon);
}
void
@ -2383,4 +2390,3 @@ main(int argc, char *argv[])
XCloseDisplay(dpy);
return EXIT_SUCCESS;
}

View file

@ -1,9 +1,3 @@
/* ____ __ */
/* / __ \_________ _/ /_____ */
/* / / / / ___/ __ `/ //_/ _ \ */
/* / /_/ / / / /_/ / ,< / __/ Clay Gomera (Drake) */
/* /_____/_/ \__,_/_/|_|\___/ My custom dwm build */
void
grid(Monitor *m) {
unsigned int i, n, cx, cy, cw, ch, aw, ah, cols, rows;
@ -31,3 +25,4 @@ grid(Monitor *m) {
i++;
}
}

View file

@ -1,10 +1,5 @@
/* ____ __ */
/* / __ \_________ _/ /_____ */
/* / / / / ___/ __ `/ //_/ _ \ */
/* / /_/ / / / /_/ / ,< / __/ Clay Gomera (Drake) */
/* /_____/_/ \__,_/_/|_|\___/ My custom dwm build */
/* cc transient.c -o transient -lX11 */
#include <stdlib.h>
#include <unistd.h>
#include <X11/Xlib.h>

View file

@ -1,9 +1,4 @@
/* ____ __ */
/* / __ \_________ _/ /_____ */
/* / / / / ___/ __ `/ //_/ _ \ */
/* / /_/ / / / /_/ / ,< / __/ Clay Gomera (Drake) */
/* /_____/_/ \__,_/_/|_|\___/ My custom dwm build */
/* See LICENSE file for copyright and license details. */
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@ -38,4 +33,3 @@ die(const char *fmt, ...) {
exit(1);
}

View file

@ -1,8 +1,4 @@
/* ____ __ */
/* / __ \_________ _/ /_____ */
/* / / / / ___/ __ `/ //_/ _ \ */
/* / /_/ / / / /_/ / ,< / __/ Clay Gomera (Drake) */
/* /_____/_/ \__,_/_/|_|\___/ My custom dwm build */
/* See LICENSE file for copyright and license details. */
#define MAX(A, B) ((A) > (B) ? (A) : (B))
#define MIN(A, B) ((A) < (B) ? (A) : (B))
@ -10,4 +6,3 @@
void die(const char *fmt, ...);
void *ecalloc(size_t nmemb, size_t size);