Compare commits

...

34 Commits
6.1 ... 6.2

Author SHA1 Message Date
cb3f58ad06 Prepare 6.2 release. 2019-02-02 04:50:42 -08:00
b69c870a30 pledge: add rpath promise for the ugly Xft font fallback 2018-06-02 17:15:42 +02:00
e78b4a9207 Makefile: just show the compiler output
Don't be fancy and just show the actual output so debugging is simpler.
2018-06-02 17:10:28 +02:00
3cd4023fb3 Do not strip at link stage
Building with debug symbols is worthless unless LDFLAGS are manually
adjusted as well.
2018-06-02 16:56:10 +02:00
f40f86fa87 Pledge on OpenBSD 2018-05-25 11:49:30 +02:00
c3a2e016bb config.def.h: ClkTagBar missing from comment
by Christopher Drelich <cd@cdrakka.com>

Patch was mangled on the ML, also adjusted the order to be the same as
the enum in dwm.c
2018-05-25 06:56:36 +02:00
c8e9479186 Function declarations in correct order.
In dwm.c function declarations are in alphabetical order except for
updategeom(). There doesn't appear to be any reason for this, so this
patch corrects that, and now all function declarations are in
alphabetical order.
2018-05-12 19:19:20 +02:00
10dfa65860 remove old TODO and BUGS entries
the bug in the dwm man page is an (ancient) Java issue.

Thanks David and quinq for the patches and feedback!
2018-05-12 19:14:19 +02:00
3bd8466e93 update README: remove mentioning the old dextra repo
Thanks Christopher Drelich <cd@cdrakka.com>
2018-03-14 21:03:11 +01:00
76c8c16d79 All functions in alphabetical order except for this one. 2018-03-14 21:02:06 +01:00
3cb34830eb ColBorder has been moved to the enum with ColFg and ColBg. 2018-03-14 17:46:48 +01:00
db2236001c dont NUL terminate _NET_WM_NAME
Reported by Kernc, thanks!

"This makes a particular program that uses libwnck [1] fail after:

    Wnck-WARNING **: Property _NET_WM_NAME contained invalid UTF-8

in this code [2] because the returned string contains a '\0' and the
documentation for g_utf8_validate() [3] explicitly states that when
string length is provided, no nul bytes are allowed."

It is not entirely clear it is incorrect, other WM's seem to not
NUL terminate it either though.
2017-12-27 13:36:53 +01:00
3756f7f6b8 sync dmenu drw.{c,h} code: use Clr* (was Scm) 2017-11-03 21:20:48 +01:00
99f78fa553 gettextprop: check result of XGetTextProperty (undefined behaviour for XFree) 2017-11-03 21:14:58 +01:00
a9b6a312a7 Set class name on status bar
This is useful for configuring compositors to ignore the status bar
window.
2017-11-03 20:40:34 +01:00
6aa8e37efe simplify isfixed conditions 2017-10-11 09:46:28 +02:00
ceac8c91ff yet another cleanup
The previous patches introduced some unclean space-based indentation
patterns. This patch fixes them.
2017-05-08 21:08:27 +02:00
5b238c8dab Don't restrict snap in mousemove
This also fixes a bug where client windows only switch to floating mode when the
mouse is dragged in one specific direction.
2017-03-28 20:23:38 +02:00
022d076054 Button passthrough when client is not focused
Before this change it is not possible to press a button in a client on the first
click if the client is not yet focused. The first click on the button would
only focus the client and a second click on the button is needed to activate it.
This situation can occur when moving the mouse over a client (therefore focusing
it) and then moving the focus to another client with keyboard shortcuts.

After this commit the behavior is fixed and button presses on unfocused clients
are passed to the client correctly.
2017-03-28 20:23:34 +02:00
2952b68db8 cleanup
- unify multi-line expression alignment style.
- unify multi-line function call alignment style.
- simplify client moving on monitor count decrease.
- clarify comment for focusin().
- remove old confusing comment about input focus fix in focusmon(). The
  explanation is already in the old commit message, so no need to keep it in the
  code.
- remove old comment describing even older state of the code in focus().
- unify comment style.
- break up some long lines.
- fix some typos and grammar.
2017-03-28 20:23:28 +02:00
bb3bd6fec3 applied Markus' tagset purge of alternative view on _NET_ACTIVE_WINDOW event 2016-12-05 10:16:46 +01:00
e63bf22948 applied Ivan Delalande's NET_SUPPORTING_WM_CHECK patch for gtk3 compatibility 2016-12-05 10:09:49 +01:00
5376947571 applied Ian Remmler's man page adjustment suggestions 2016-12-05 10:05:00 +01:00
975c898376 applied Markus' decouple color-scheme patch 2016-12-05 10:01:33 +01:00
a137a86a23 applied Markus' clarify status text padding patch 2016-12-05 09:54:20 +01:00
839c7f6939 LICENSE: update people 2016-11-15 17:35:33 +01:00
7a59cd1457 dwm.1: add keybinding for spawning dmenu 2016-11-15 17:35:30 +01:00
24849acada die() on calloc failure
thanks Markus Teich and David!
2016-11-05 11:34:52 +01:00
ab9571bbc5 die() consistency: always add newline 2016-08-12 14:36:35 +02:00
56a31dc4a7 config.def.h: style improvement, use color Scheme enum 2016-06-28 18:04:56 +02:00
7af4d439bd import new drw from libsl and minor fixes.
- better scaling for occupied tag squares.
- draw statusline first to omitt some complicated calculations.
2016-06-26 13:52:36 +02:00
cd2d7549b3 Configure geometry before applying rules
Configuring geometry before applying rules makes it possible to have
more complex constraints in applyrules that depend on the initial window
dimensions and location.
2016-06-24 05:38:58 +02:00
3465bed290 fix fullscreen clients not resized on X display resolution change
patch provided by Bert Münnich <ber.t_AT_posteo.de>, thanks!
2015-12-19 20:25:26 +01:00
4ec3a673ff Shut up glibc about _BSD_SOURCE being deprecated 2015-12-19 20:20:26 +01:00
13 changed files with 415 additions and 439 deletions

44
BUGS
View File

@ -1,44 +0,0 @@
---
18:17 < Biolunar> when i change my resolution in dwm (to a smaller one) and then back to the native, the top bar is not repainted. that's since 5.7.2, in 5.6 it worked fine
18:19 < Biolunar> is it just happening to me or a (known) bug?
18:24 < Biolunar> and in addition, mplayers fullscreen is limited to the small resolution after i changed it back to the native
reproducible with xrandr -s but not with --output and --mode, strange
---
yet another corner case:
open a terminal, focus another monitor, but without moving the mouse
pointer there
if there is no client on the other monitor to get the focus, then the
terminal will be unfocused but it will accept input
---
Donald Allen reported this:
starting emacs from dmenu in archlinux results in missing configure of emacs, but mod1-space or mod1-shift-space fix this problem. this problem is new and did not happen in 1.6 xorg servers
---
voltaic reports this:
When I use two monitors, one larger in resolution than the other, the
bar is drawn using the smaller x-dimension on both screens. I think
what's happening is that there are two bars drawn, but the short bar
is always on top of the long bar such that I can't see the information
under the short bar. If I switch to the small screen, hide the short
bar, and then switch to the large screen, the long bar is drawn
correctly.
A similar problem occurs when I have started dwm on a small resolution
monitor (laptop screen) and then I switch to a large external display.
When I do this, the bar itself is drawn for the original smaller
resolution, but the information to be printed on the bar is
right-aligned for a longer bar. So what I see is a bar that has the
right hand side of it cut-off. See attached screenshot.
I am using standard options for xrandr such as --output VGA1 --auto, etc.
---

18
LICENSE
View File

@ -1,18 +1,22 @@
MIT/X Consortium License
© 2006-2014 Anselm R Garbe <anselm@garbe.us>
© 2010-2014 Hiltjo Posthuma <hiltjo@codemadness.org>
© 2007-2011 Peter Hartlich <sgkkr at hartlich dot com>
© 2010-2011 Connor Lane Smith <cls@lubutu.com>
© 2006-2019 Anselm R Garbe <anselm@garbe.ca>
© 2006-2009 Jukka Salmi <jukka at salmi dot ch>
© 2007-2009 Premysl Hruby <dfenze at gmail dot com>
© 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
© 2007-2011 Peter Hartlich <sgkkr at hartlich dot com>
© 2007-2009 Szabolcs Nagy <nszabolcs at gmail dot com>
© 2007-2009 Christof Musik <christof at sendfax dot de>
© 2009 Mate Nagy <mnagy at port70 dot net>
© 2007-2009 Premysl Hruby <dfenze at gmail dot com>
© 2007-2008 Enno Gottox Boland <gottox at s01 dot de>
© 2008 Martin Hurton <martin dot hurton at gmail dot com>
© 2008 Neale Pickett <neale dot woozle dot org>
© 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
© 2009 Mate Nagy <mnagy at port70 dot net>
© 2010-2016 Hiltjo Posthuma <hiltjo@codemadness.org>
© 2010-2012 Connor Lane Smith <cls@lubutu.com>
© 2011 Christoph Lohmann <20h@r-36.net>
© 2015-2016 Quentin Rameau <quinq@fifth.space>
© 2015-2016 Eric Pruitt <eric.pruitt@gmail.com>
© 2016-2017 Markus Teich <markus.teich@stusta.mhn.de>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),

View File

@ -15,46 +15,37 @@ options:
@echo "CC = ${CC}"
.c.o:
@echo CC $<
@${CC} -c ${CFLAGS} $<
${CC} -c ${CFLAGS} $<
${OBJ}: config.h config.mk
config.h:
@echo creating $@ from config.def.h
@cp config.def.h $@
cp config.def.h $@
dwm: ${OBJ}
@echo CC -o $@
@${CC} -o $@ ${OBJ} ${LDFLAGS}
${CC} -o $@ ${OBJ} ${LDFLAGS}
clean:
@echo cleaning
@rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz
rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz
dist: clean
@echo creating dist tarball
@mkdir -p dwm-${VERSION}
@cp -R LICENSE TODO BUGS Makefile README config.def.h config.mk \
mkdir -p dwm-${VERSION}
cp -R LICENSE Makefile README config.def.h config.mk\
dwm.1 drw.h util.h ${SRC} dwm.png transient.c dwm-${VERSION}
@tar -cf dwm-${VERSION}.tar dwm-${VERSION}
@gzip dwm-${VERSION}.tar
@rm -rf dwm-${VERSION}
tar -cf dwm-${VERSION}.tar dwm-${VERSION}
gzip dwm-${VERSION}.tar
rm -rf dwm-${VERSION}
install: all
@echo installing executable file to ${DESTDIR}${PREFIX}/bin
@mkdir -p ${DESTDIR}${PREFIX}/bin
@cp -f dwm ${DESTDIR}${PREFIX}/bin
@chmod 755 ${DESTDIR}${PREFIX}/bin/dwm
@echo installing manual page to ${DESTDIR}${MANPREFIX}/man1
@mkdir -p ${DESTDIR}${MANPREFIX}/man1
@sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1
@chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1
mkdir -p ${DESTDIR}${PREFIX}/bin
cp -f dwm ${DESTDIR}${PREFIX}/bin
chmod 755 ${DESTDIR}${PREFIX}/bin/dwm
mkdir -p ${DESTDIR}${MANPREFIX}/man1
sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1
chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1
uninstall:
@echo removing executable file from ${DESTDIR}${PREFIX}/bin
@rm -f ${DESTDIR}${PREFIX}/bin/dwm
@echo removing manual page from ${DESTDIR}${MANPREFIX}/man1
@rm -f ${DESTDIR}${MANPREFIX}/man1/dwm.1
rm -f ${DESTDIR}${PREFIX}/bin/dwm\
${DESTDIR}${MANPREFIX}/man1/dwm.1
.PHONY: all options clean dist install uninstall

3
README
View File

@ -18,9 +18,6 @@ necessary as root):
make clean install
If you are going to use the default bluegray color scheme it is highly
recommended to also install the bluegray files shipped in the dextra package.
Running dwm
-----------

4
TODO
View File

@ -1,4 +0,0 @@
- add a flag to Key to execute the command on release (needed for commands
affecting the keyboard grab, see scrot -s for example)
- add updategeom() hook for external tools like dzen
- consider onscreenkeyboard hooks for tablet deployment

View File

@ -1,20 +1,22 @@
/* See LICENSE file for copyright and license details. */
/* appearance */
static const char *fonts[] = {
"monospace:size=10"
};
static const char dmenufont[] = "monospace:size=10";
static const char normbordercolor[] = "#444444";
static const char normbgcolor[] = "#222222";
static const char normfgcolor[] = "#bbbbbb";
static const char selbordercolor[] = "#005577";
static const char selbgcolor[] = "#005577";
static const char selfgcolor[] = "#eeeeee";
static const unsigned int borderpx = 1; /* border pixel of windows */
static const unsigned int snap = 32; /* snap pixel */
static const int showbar = 1; /* 0 means no bar */
static const int topbar = 1; /* 0 means bottom bar */
static const char *fonts[] = { "monospace:size=10" };
static const char dmenufont[] = "monospace:size=10";
static const char col_gray1[] = "#222222";
static const char col_gray2[] = "#444444";
static const char col_gray3[] = "#bbbbbb";
static const char col_gray4[] = "#eeeeee";
static const char col_cyan[] = "#005577";
static const char *colors[][3] = {
/* fg bg border */
[SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
[SchemeSel] = { col_gray4, col_cyan, col_cyan },
};
/* tagging */
static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
@ -54,7 +56,7 @@ static const Layout layouts[] = {
/* commands */
static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */
static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", normbgcolor, "-nf", normfgcolor, "-sb", selbgcolor, "-sf", selfgcolor, NULL };
static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
static const char *termcmd[] = { "st", NULL };
static Key keys[] = {
@ -95,7 +97,7 @@ static Key keys[] = {
};
/* button definitions */
/* click can be ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
static Button buttons[] = {
/* click event mask button function argument */
{ ClkLtSymbol, 0, Button1, setlayout, {0} },

View File

@ -1,5 +1,5 @@
# dwm version
VERSION = 6.1
VERSION = 6.2
# Customize below to fit your system
@ -25,10 +25,10 @@ INCS = -I${X11INC} -I${FREETYPEINC}
LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS}
# flags
CPPFLAGS = -D_BSD_SOURCE -D_POSIX_C_SOURCE=2 -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=2 -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
#CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS}
CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS}
LDFLAGS = -s ${LIBS}
LDFLAGS = ${LIBS}
# Solaris
#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\"

273
drw.c
View File

@ -63,9 +63,8 @@ utf8decode(const char *c, long *u, size_t clen)
Drw *
drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h)
{
Drw *drw;
Drw *drw = ecalloc(1, sizeof(Drw));
drw = ecalloc(1, sizeof(Drw));
drw->dpy = dpy;
drw->screen = screen;
drw->root = root;
@ -73,7 +72,6 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h
drw->h = h;
drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
drw->gc = XCreateGC(dpy, root, 0, NULL);
drw->fontcount = 0;
XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
return drw;
@ -82,6 +80,9 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h
void
drw_resize(Drw *drw, unsigned int w, unsigned int h)
{
if (!drw)
return;
drw->w = w;
drw->h = h;
if (drw->drawable)
@ -92,84 +93,69 @@ drw_resize(Drw *drw, unsigned int w, unsigned int h)
void
drw_free(Drw *drw)
{
size_t i;
for (i = 0; i < drw->fontcount; i++)
drw_font_free(drw->fonts[i]);
XFreePixmap(drw->dpy, drw->drawable);
XFreeGC(drw->dpy, drw->gc);
free(drw);
}
/* This function is an implementation detail. Library users should use
* drw_font_create instead.
* drw_fontset_create instead.
*/
static Fnt *
drw_font_xcreate(Drw *drw, const char *fontname, FcPattern *fontpattern)
xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
{
Fnt *font;
XftFont *xfont = NULL;
FcPattern *pattern = NULL;
if (fontname) {
/* Using the pattern found at font->xfont->pattern does not yield same
* the same substitution results as using the pattern returned by
/* Using the pattern found at font->xfont->pattern does not yield the
* same substitution results as using the pattern returned by
* FcNameParse; using the latter results in the desired fallback
* behaviour whereas the former just results in
* missing-character-rectangles being drawn, at least with some fonts.
*/
* behaviour whereas the former just results in missing-character
* rectangles being drawn, at least with some fonts. */
if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) {
fprintf(stderr, "error, cannot load font: '%s'\n", fontname);
fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname);
return NULL;
}
if (!(pattern = FcNameParse((FcChar8 *) fontname))) {
fprintf(stderr, "error, cannot load font: '%s'\n", fontname);
fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname);
XftFontClose(drw->dpy, xfont);
return NULL;
}
} else if (fontpattern) {
if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
fprintf(stderr, "error, cannot load font pattern.\n");
fprintf(stderr, "error, cannot load font from pattern.\n");
return NULL;
}
} else {
die("no font specified.\n");
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;
font->ascent = xfont->ascent;
font->descent = xfont->descent;
font->h = font->ascent + font->descent;
font->h = xfont->ascent + xfont->descent;
font->dpy = drw->dpy;
return font;
}
Fnt*
drw_font_create(Drw *drw, const char *fontname)
{
return drw_font_xcreate(drw, fontname, NULL);
}
void
drw_load_fonts(Drw* drw, const char *fonts[], size_t fontcount)
{
size_t i;
Fnt *font;
for (i = 0; i < fontcount; i++) {
if (drw->fontcount >= DRW_FONT_CACHE_SIZE) {
die("font cache exhausted.\n");
} else if ((font = drw_font_xcreate(drw, fonts[i], NULL))) {
drw->fonts[drw->fontcount++] = font;
}
}
}
void
drw_font_free(Fnt *font)
static void
xfont_free(Fnt *font)
{
if (!font)
return;
@ -179,55 +165,98 @@ drw_font_free(Fnt *font)
free(font);
}
Clr *
drw_clr_create(Drw *drw, const char *clrname)
Fnt*
drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount)
{
Clr *clr;
Fnt *cur, *ret = NULL;
size_t i;
if (!drw || !fonts)
return NULL;
for (i = 1; i <= fontcount; i++) {
if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) {
cur->next = ret;
ret = cur;
}
}
return (drw->fonts = ret);
}
void
drw_fontset_free(Fnt *font)
{
if (font) {
drw_fontset_free(font->next);
xfont_free(font);
}
}
void
drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
{
if (!drw || !dest || !clrname)
return;
clr = ecalloc(1, sizeof(Clr));
if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen),
clrname, &clr->rgb))
die("error, cannot allocate color '%s'\n", clrname);
clr->pix = clr->rgb.pixel;
clrname, dest))
die("error, cannot allocate color '%s'", clrname);
}
return clr;
/* Wrapper to create color schemes. The caller has to call free(3) on the
* returned color scheme when done using it. */
Clr *
drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
{
size_t i;
Clr *ret;
/* need at least two colors for a scheme */
if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor))))
return NULL;
for (i = 0; i < clrcount; i++)
drw_clr_create(drw, &ret[i], clrnames[i]);
return ret;
}
void
drw_clr_free(Clr *clr)
drw_setfontset(Drw *drw, Fnt *set)
{
free(clr);
if (drw)
drw->fonts = set;
}
void
drw_setscheme(Drw *drw, ClrScheme *scheme)
drw_setscheme(Drw *drw, Clr *scm)
{
drw->scheme = scheme;
if (drw)
drw->scheme = scm;
}
void
drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int empty, int invert)
drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert)
{
if (!drw->scheme)
if (!drw || !drw->scheme)
return;
XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme->bg->pix : drw->scheme->fg->pix);
XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel);
if (filled)
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w + 1, h + 1);
else if (empty)
XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
else
XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1);
}
int
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int invert)
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 tx, ty, th;
Extnts tex;
int ty;
unsigned int ew;
XftDraw *d = NULL;
Fnt *curfont, *nextfont;
Fnt *usedfont, *curfont, *nextfont;
size_t i, len;
int utf8strlen, utf8charlen, render;
int utf8strlen, utf8charlen, render = x || y || w || h;
long utf8codepoint = 0;
const char *utf8str;
FcCharSet *fccharset;
@ -236,66 +265,67 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *tex
XftResult result;
int charexists = 0;
if (!drw->scheme || !drw->fontcount)
if (!drw || (render && !drw->scheme) || !text || !drw->fonts)
return 0;
if (!(render = x || y || w || h)) {
if (!render) {
w = ~w;
} else {
XSetForeground(drw->dpy, drw->gc, invert ?
drw->scheme->fg->pix : drw->scheme->bg->pix);
XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
d = XftDrawCreate(drw->dpy, drw->drawable,
DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen));
x += lpad;
w -= lpad;
}
curfont = drw->fonts[0];
usedfont = drw->fonts;
while (1) {
utf8strlen = 0;
utf8str = text;
nextfont = NULL;
while (*text) {
utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
for (i = 0; i < drw->fontcount; i++) {
charexists = charexists || XftCharExists(drw->dpy, drw->fonts[i]->xfont, utf8codepoint);
for (curfont = drw->fonts; curfont; curfont = curfont->next) {
charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
if (charexists) {
if (drw->fonts[i] == curfont) {
if (curfont == usedfont) {
utf8strlen += utf8charlen;
text += utf8charlen;
} else {
nextfont = drw->fonts[i];
nextfont = curfont;
}
break;
}
}
if (!charexists || (nextfont && nextfont != curfont))
if (!charexists || nextfont)
break;
else
charexists = 0;
}
if (utf8strlen) {
drw_font_getexts(curfont, utf8str, utf8strlen, &tex);
drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL);
/* shorten text if necessary */
for (len = MIN(utf8strlen, (sizeof buf) - 1); len && (tex.w > w - drw->fonts[0]->h || w < drw->fonts[0]->h); len--)
drw_font_getexts(curfont, utf8str, len, &tex);
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] = '.');
for (i = len; i && i > len - 3; buf[--i] = '.')
; /* NOP */
if (render) {
th = curfont->ascent + curfont->descent;
ty = y + (h / 2) - (th / 2) + curfont->ascent;
tx = x + (h / 2);
XftDrawStringUtf8(d, invert ? &drw->scheme->bg->rgb : &drw->scheme->fg->rgb, curfont->xfont, tx, ty, (XftChar8 *)buf, len);
ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
usedfont->xfont, x, ty, (XftChar8 *)buf, len);
}
x += tex.w;
w -= tex.w;
x += ew;
w -= ew;
}
}
@ -303,28 +333,24 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *tex
break;
} else if (nextfont) {
charexists = 0;
curfont = nextfont;
usedfont = nextfont;
} else {
/* Regardless of whether or not a fallback font is found, the
* character must be drawn.
*/
* character must be drawn. */
charexists = 1;
if (drw->fontcount >= DRW_FONT_CACHE_SIZE)
continue;
fccharset = FcCharSetCreate();
FcCharSetAddChar(fccharset, utf8codepoint);
if (!drw->fonts[0]->pattern) {
/* Refer to the comment in drw_font_xcreate for more
* information. */
die("the first font in the cache must be loaded from a font string.\n");
if (!drw->fonts->pattern) {
/* Refer to the comment in xfont_create for more information. */
die("the first font in the cache must be loaded from a font string.");
}
fcpattern = FcPatternDuplicate(drw->fonts[0]->pattern);
fcpattern = FcPatternDuplicate(drw->fonts->pattern);
FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
FcPatternAddBool(fcpattern, FC_COLOR, FcFalse);
FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
FcDefaultSubstitute(fcpattern);
@ -334,12 +360,14 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *tex
FcPatternDestroy(fcpattern);
if (match) {
curfont = drw_font_xcreate(drw, NULL, match);
if (curfont && XftCharExists(drw->dpy, curfont->xfont, utf8codepoint)) {
drw->fonts[drw->fontcount++] = curfont;
usedfont = xfont_create(drw, NULL, match);
if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) {
for (curfont = drw->fonts; curfont->next; curfont = curfont->next)
; /* NOP */
curfont->next = usedfont;
} else {
drw_font_free(curfont);
curfont = drw->fonts[0];
xfont_free(usedfont);
usedfont = drw->fonts;
}
}
}
@ -347,34 +375,40 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *tex
if (d)
XftDrawDestroy(d);
return x;
return x + (render ? w : 0);
}
void
drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
{
if (!drw)
return;
XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
XSync(drw->dpy, False);
}
unsigned int
drw_fontset_getwidth(Drw *drw, const char *text)
{
if (!drw || !drw->fonts || !text)
return 0;
return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
}
void
drw_font_getexts(Fnt *font, const char *text, unsigned int len, Extnts *tex)
drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h)
{
XGlyphInfo ext;
if (!font || !text)
return;
XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
tex->h = font->h;
tex->w = ext.xOff;
}
unsigned int
drw_font_getexts_width(Fnt *font, const char *text, unsigned int len)
{
Extnts tex;
drw_font_getexts(font, text, len, &tex);
return tex.w;
if (w)
*w = ext.xOff;
if (h)
*h = font->h;
}
Cur *
@ -382,7 +416,9 @@ drw_cur_create(Drw *drw, int shape)
{
Cur *cur;
cur = ecalloc(1, sizeof(Cur));
if (!drw || !(cur = ecalloc(1, sizeof(Cur))))
return NULL;
cur->cursor = XCreateFontCursor(drw->dpy, shape);
return cur;
@ -393,6 +429,7 @@ drw_cur_free(Drw *drw, Cur *cursor)
{
if (!cursor)
return;
XFreeCursor(drw->dpy, cursor->cursor);
free(cursor);
}

63
drw.h
View File

@ -1,29 +1,19 @@
/* See LICENSE file for copyright and license details. */
#define DRW_FONT_CACHE_SIZE 32
typedef struct {
unsigned long pix;
XftColor rgb;
} Clr;
typedef struct {
Cursor cursor;
} Cur;
typedef struct {
typedef struct Fnt {
Display *dpy;
int ascent;
int descent;
unsigned int h;
XftFont *xfont;
FcPattern *pattern;
struct Fnt *next;
} Fnt;
typedef struct {
Clr *fg;
Clr *bg;
Clr *border;
} ClrScheme;
enum { ColFg, ColBg, ColBorder }; /* Clr scheme index */
typedef XftColor Clr;
typedef struct {
unsigned int w, h;
@ -32,43 +22,36 @@ typedef struct {
Window root;
Drawable drawable;
GC gc;
ClrScheme *scheme;
size_t fontcount;
Fnt *fonts[DRW_FONT_CACHE_SIZE];
Clr *scheme;
Fnt *fonts;
} Drw;
typedef struct {
unsigned int w;
unsigned int h;
} Extnts;
/* Drawable abstraction */
Drw *drw_create(Display *, int, Window, unsigned int, unsigned int);
void drw_resize(Drw *, unsigned int, unsigned int);
void drw_free(Drw *);
Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h);
void drw_resize(Drw *drw, unsigned int w, unsigned int h);
void drw_free(Drw *drw);
/* Fnt abstraction */
Fnt *drw_font_create(Drw *, const char *);
void drw_load_fonts(Drw *, const char *[], size_t);
void drw_font_free(Fnt *);
void drw_font_getexts(Fnt *, const char *, unsigned int, Extnts *);
unsigned int drw_font_getexts_width(Fnt *, const char *, unsigned int);
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);
void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h);
/* Colour abstraction */
Clr *drw_clr_create(Drw *, const char *);
void drw_clr_free(Clr *);
/* Colorscheme abstraction */
void drw_clr_create(Drw *drw, Clr *dest, const char *clrname);
Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount);
/* Cursor abstraction */
Cur *drw_cur_create(Drw *, int);
void drw_cur_free(Drw *, Cur *);
Cur *drw_cur_create(Drw *drw, int shape);
void drw_cur_free(Drw *drw, Cur *cursor);
/* Drawing context manipulation */
void drw_setfont(Drw *, Fnt *);
void drw_setscheme(Drw *, ClrScheme *);
void drw_setfontset(Drw *drw, Fnt *set);
void drw_setscheme(Drw *drw, Clr *scm);
/* Drawing functions */
void drw_rect(Drw *, int, int, unsigned int, unsigned int, int, int, int);
int drw_text(Drw *, int, int, unsigned int, unsigned int, const char *, int);
void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert);
int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert);
/* Map functions */
void drw_map(Drw *, Window, int, int, unsigned int, unsigned int);
void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);

26
dwm.1
View File

@ -10,8 +10,9 @@ and floating layouts. Either layout can be applied dynamically, optimising the
environment for the application in use and the task performed.
.P
In tiled layouts windows are managed in a master and stacking area. The master
area contains the window which currently needs most attention, whereas the
stacking area contains all other windows. In monocle layout all windows are
area on the left contains one window by default, and the stacking area on the
right contains all other windows. The number of master area windows can be
adjusted from zero to an arbitrary number. In monocle layout all windows are
maximised to the screen size. In floating layout windows can be resized and
moved freely. Dialog windows are always managed floating, regardless of the
layout applied.
@ -59,6 +60,11 @@ click on a tag label adds/removes that tag to/from the focused window.
Start
.BR st(1).
.TP
.B Mod1\-p
Spawn
.BR dmenu(1)
for launching other programs.
.TP
.B Mod1\-,
Focus previous screen, if any.
.TP
@ -93,10 +99,10 @@ Focus next window.
Focus previous window.
.TP
.B Mod1\-i
Increase clients in master area.
Increase number of windows in master area.
.TP
.B Mod1\-d
Decrease clients in master area.
Decrease number of windows in master area.
.TP
.B Mod1\-l
Increase master area size.
@ -152,7 +158,7 @@ code. This keeps it fast, secure and simple.
.SH SEE ALSO
.BR dmenu (1),
.BR st (1)
.SH BUGS
.SH ISSUES
Java applications which use the XToolkit/XAWT backend may draw grey windows
only. The XToolkit/XAWT backend breaks ICCCM-compliance in recent JDK 1.5 and early
JDK 1.6 versions, because it assumes a reparenting window manager. Possible workarounds
@ -166,11 +172,5 @@ or
(to pretend that a non-reparenting window manager is running that the
XToolkit/XAWT backend can recognize) or when using OpenJDK setting the environment variable
.BR _JAVA_AWT_WM_NONREPARENTING=1 .
.P
GTK 2.10.9+ versions contain a broken
.BR Save\-As
file dialog implementation,
which requests to reconfigure its window size in an endless loop. However, its
window is still respondable during this state, so you can simply ignore the flicker
until a new GTK version appears, which will fix this bug, approximately
GTK 2.10.12+ versions.
.SH BUGS
Send all bug reports with a patch to hackers@suckless.org.

340
dwm.c
View File

@ -3,7 +3,7 @@
* 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
* events about window (dis-)appearance. Only one X connection at a time is
* events about window (dis-)appearance. Only one X connection at a time is
* allowed to select for this event mask.
*
* The event handlers of dwm are organized in an array which is accessed
@ -11,7 +11,7 @@
* in O(1) time.
*
* Each child of the root window is called a client, except windows which have
* set the override_redirect flag. Clients are organized in a linked client
* set the override_redirect flag. Clients are organized in a linked client
* list on each monitor, the focus history is remembered through a stack list
* on each monitor. Each client contains a bit array to indicate the tags of a
* client.
@ -55,12 +55,12 @@
#define WIDTH(X) ((X)->w + 2 * (X)->bw)
#define HEIGHT(X) ((X)->h + 2 * (X)->bw)
#define TAGMASK ((1 << LENGTH(tags)) - 1)
#define TEXTW(X) (drw_text(drw, 0, 0, 0, 0, (X), 0) + drw->fonts[0]->h)
#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
/* enums */
enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
enum { SchemeNorm, SchemeSel, SchemeLast }; /* color schemes */
enum { NetSupported, NetWMName, NetWMState,
enum { SchemeNorm, SchemeSel }; /* color schemes */
enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
NetWMFullscreen, NetActiveWindow, NetWMWindowType,
NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
@ -152,7 +152,6 @@ static void buttonpress(XEvent *e);
static void checkotherwm(void);
static void cleanup(void);
static void cleanupmon(Monitor *mon);
static void clearurgent(Client *c);
static void clientmessage(XEvent *e);
static void configure(Client *c);
static void configurenotify(XEvent *e);
@ -203,6 +202,7 @@ static void setfullscreen(Client *c, int fullscreen);
static void setlayout(const Arg *arg);
static void setmfact(const Arg *arg);
static void setup(void);
static void seturgent(Client *c, int urg);
static void showhide(Client *c);
static void sigchld(int unused);
static void spawn(const Arg *arg);
@ -216,15 +216,15 @@ static void toggleview(const Arg *arg);
static void unfocus(Client *c, int setfocus);
static void unmanage(Client *c, int destroyed);
static void unmapnotify(XEvent *e);
static int updategeom(void);
static void updatebarpos(Monitor *m);
static void updatebars(void);
static void updateclientlist(void);
static int updategeom(void);
static void updatenumlockmask(void);
static void updatesizehints(Client *c);
static void updatestatus(void);
static void updatewindowtype(Client *c);
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);
@ -240,6 +240,7 @@ static char stext[256];
static int screen;
static int sw, sh; /* X display screen geometry width, height */
static int bh, blw = 0; /* bar geometry */
static int lrpad; /* sum of left and right padding for text */
static int (*xerrorxlib)(Display *, XErrorEvent *);
static unsigned int numlockmask = 0;
static void (*handler[LASTEvent]) (XEvent *) = {
@ -261,11 +262,11 @@ static void (*handler[LASTEvent]) (XEvent *) = {
static Atom wmatom[WMLast], netatom[NetLast];
static int running = 1;
static Cur *cursor[CurLast];
static ClrScheme scheme[SchemeLast];
static Clr **scheme;
static Display *dpy;
static Drw *drw;
static Monitor *mons, *selmon;
static Window root;
static Window root, wmcheckwin;
/* configuration, allows nested code to access above variables */
#include "config.h"
@ -444,6 +445,8 @@ buttonpress(XEvent *e)
click = ClkWinTitle;
} else if ((c = wintoclient(ev->window))) {
focus(c);
restack(selmon);
XAllowEvents(dpy, ReplayPointer, CurrentTime);
click = ClkClientWin;
}
for (i = 0; i < LENGTH(buttons); i++)
@ -481,11 +484,9 @@ cleanup(void)
cleanupmon(mons);
for (i = 0; i < CurLast; i++)
drw_cur_free(drw, cursor[i]);
for (i = 0; i < SchemeLast; i++) {
drw_clr_free(scheme[i].border);
drw_clr_free(scheme[i].bg);
drw_clr_free(scheme[i].fg);
}
for (i = 0; i < LENGTH(colors); i++)
free(scheme[i]);
XDestroyWindow(dpy, wmcheckwin);
drw_free(drw);
XSync(dpy, False);
XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
@ -508,19 +509,6 @@ cleanupmon(Monitor *mon)
free(mon);
}
void
clearurgent(Client *c)
{
XWMHints *wmh;
c->isurgent = 0;
if (!(wmh = XGetWMHints(dpy, c->win)))
return;
wmh->flags &= ~XUrgencyHint;
XSetWMHints(dpy, c->win, wmh);
XFree(wmh);
}
void
clientmessage(XEvent *e)
{
@ -530,15 +518,13 @@ clientmessage(XEvent *e)
if (!c)
return;
if (cme->message_type == netatom[NetWMState]) {
if (cme->data.l[1] == netatom[NetWMFullscreen] || cme->data.l[2] == netatom[NetWMFullscreen])
if (cme->data.l[1] == netatom[NetWMFullscreen]
|| cme->data.l[2] == netatom[NetWMFullscreen])
setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */
|| (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen)));
|| (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen)));
} else if (cme->message_type == netatom[NetActiveWindow]) {
if (!ISVISIBLE(c)) {
c->mon->seltags ^= 1;
c->mon->tagset[c->mon->seltags] = c->tags;
}
pop(c);
if (c != selmon->sel && !c->isurgent)
seturgent(c, 1);
}
}
@ -565,6 +551,7 @@ void
configurenotify(XEvent *e)
{
Monitor *m;
Client *c;
XConfigureEvent *ev = &e->xconfigure;
int dirty;
@ -576,8 +563,12 @@ configurenotify(XEvent *e)
if (updategeom() || dirty) {
drw_resize(drw, sw, bh);
updatebars();
for (m = mons; m; m = m->next)
for (m = mons; m; m = m->next) {
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);
}
focus(NULL);
arrange(NULL);
}
@ -704,11 +695,18 @@ dirtomon(int dir)
void
drawbar(Monitor *m)
{
int x, xx, w, dx;
int x, w, sw = 0;
int boxs = drw->fonts->h / 9;
int boxw = drw->fonts->h / 6 + 2;
unsigned int i, occ = 0, urg = 0;
Client *c;
dx = (drw->fonts[0]->ascent + drw->fonts[0]->descent + 2) / 4;
/* 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]);
sw = TEXTW(stext) - lrpad + 2; /* 2px right padding */
drw_text(drw, m->ww - sw, 0, sw, bh, 0, stext, 0);
}
for (c = m->clients; c; c = c->next) {
occ |= c->tags;
@ -718,36 +716,27 @@ drawbar(Monitor *m)
x = 0;
for (i = 0; i < LENGTH(tags); i++) {
w = TEXTW(tags[i]);
drw_setscheme(drw, m->tagset[m->seltags] & 1 << i ? &scheme[SchemeSel] : &scheme[SchemeNorm]);
drw_text(drw, x, 0, w, bh, tags[i], urg & 1 << i);
drw_rect(drw, x + 1, 1, dx, dx, m == selmon && selmon->sel && selmon->sel->tags & 1 << i,
occ & 1 << i, urg & 1 << i);
drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]);
drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i);
if (occ & 1 << i)
drw_rect(drw, x + boxs, boxs, boxw, boxw,
m == selmon && selmon->sel && selmon->sel->tags & 1 << i,
urg & 1 << i);
x += w;
}
w = blw = TEXTW(m->ltsymbol);
drw_setscheme(drw, &scheme[SchemeNorm]);
drw_text(drw, x, 0, w, bh, m->ltsymbol, 0);
x += w;
xx = x;
if (m == selmon) { /* status is only drawn on selected monitor */
w = TEXTW(stext);
x = m->ww - w;
if (x < xx) {
x = xx;
w = m->ww - xx;
}
drw_text(drw, x, 0, w, bh, stext, 0);
} else
x = m->ww;
if ((w = x - xx) > bh) {
x = xx;
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 (m->sel) {
drw_setscheme(drw, m == selmon ? &scheme[SchemeSel] : &scheme[SchemeNorm]);
drw_text(drw, x, 0, w, bh, m->sel->name, 0);
drw_rect(drw, x + 1, 1, dx, dx, m->sel->isfixed, m->sel->isfloating, 0);
drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]);
drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0);
if (m->sel->isfloating)
drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0);
} else {
drw_setscheme(drw, &scheme[SchemeNorm]);
drw_rect(drw, x, 0, w, bh, 1, 0, 1);
drw_setscheme(drw, scheme[SchemeNorm]);
drw_rect(drw, x, 0, w, bh, 1, 1);
}
}
drw_map(drw, m->barwin, 0, 0, m->ww, bh);
@ -796,18 +785,17 @@ focus(Client *c)
{
if (!c || !ISVISIBLE(c))
for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext);
/* was if (selmon->sel) */
if (selmon->sel && selmon->sel != c)
unfocus(selmon->sel, 0);
if (c) {
if (c->mon != selmon)
selmon = c->mon;
if (c->isurgent)
clearurgent(c);
seturgent(c, 0);
detachstack(c);
attachstack(c);
grabbuttons(c, 1);
XSetWindowBorder(dpy, c->win, scheme[SchemeSel].border->pix);
XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel);
setfocus(c);
} else {
XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
@ -817,7 +805,7 @@ focus(Client *c)
drawbars();
}
/* there are some broken focus acquiring clients */
/* there are some broken focus acquiring clients needing extra handling */
void
focusin(XEvent *e)
{
@ -836,8 +824,7 @@ focusmon(const Arg *arg)
return;
if ((m = dirtomon(arg->i)) == selmon)
return;
unfocus(selmon->sel, 0); /* s/1/0/ fixes input focus issues
in gedit and anjuta */
unfocus(selmon->sel, 0);
selmon = m;
focus(NULL);
}
@ -877,7 +864,7 @@ getatomprop(Client *c, Atom prop)
Atom da, atom = None;
if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM,
&da, &di, &dl, &dl, &p) == Success && p) {
&da, &di, &dl, &dl, &p) == Success && p) {
atom = *(Atom *)p;
XFree(p);
}
@ -904,7 +891,7 @@ getstate(Window w)
Atom real;
if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState],
&real, &format, &n, &extra, (unsigned char **)&p) != Success)
&real, &format, &n, &extra, (unsigned char **)&p) != Success)
return -1;
if (n != 0)
result = *p;
@ -922,8 +909,7 @@ gettextprop(Window w, Atom atom, char *text, unsigned int size)
if (!text || size == 0)
return 0;
text[0] = '\0';
XGetTextProperty(dpy, w, &name, atom);
if (!name.nitems)
if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems)
return 0;
if (name.encoding == XA_STRING)
strncpy(text, (char *)name.value, size - 1);
@ -946,17 +932,16 @@ grabbuttons(Client *c, int focused)
unsigned int i, j;
unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
if (focused) {
for (i = 0; i < LENGTH(buttons); i++)
if (buttons[i].click == ClkClientWin)
for (j = 0; j < LENGTH(modifiers); j++)
XGrabButton(dpy, buttons[i].button,
buttons[i].mask | modifiers[j],
c->win, False, BUTTONMASK,
GrabModeAsync, GrabModeSync, None, None);
} else
if (!focused)
XGrabButton(dpy, AnyButton, AnyModifier, c->win, False,
BUTTONMASK, GrabModeAsync, GrabModeSync, None, None);
BUTTONMASK, GrabModeSync, GrabModeSync, None, None);
for (i = 0; i < LENGTH(buttons); i++)
if (buttons[i].click == ClkClientWin)
for (j = 0; j < LENGTH(modifiers); j++)
XGrabButton(dpy, buttons[i].button,
buttons[i].mask | modifiers[j],
c->win, False, BUTTONMASK,
GrabModeAsync, GrabModeSync, None, None);
}
}
@ -974,7 +959,7 @@ grabkeys(void)
if ((code = XKeysymToKeycode(dpy, keys[i].keysym)))
for (j = 0; j < LENGTH(modifiers); j++)
XGrabKey(dpy, code, keys[i].mod | modifiers[j], root,
True, GrabModeAsync, GrabModeAsync);
True, GrabModeAsync, GrabModeAsync);
}
}
@ -1038,6 +1023,13 @@ manage(Window w, XWindowAttributes *wa)
c = ecalloc(1, sizeof(Client));
c->win = w;
/* geometry */
c->x = c->oldx = wa->x;
c->y = c->oldy = wa->y;
c->w = c->oldw = wa->width;
c->h = c->oldh = wa->height;
c->oldbw = wa->border_width;
updatetitle(c);
if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
c->mon = t->mon;
@ -1046,12 +1038,6 @@ manage(Window w, XWindowAttributes *wa)
c->mon = selmon;
applyrules(c);
}
/* geometry */
c->x = c->oldx = wa->x;
c->y = c->oldy = wa->y;
c->w = c->oldw = wa->width;
c->h = c->oldh = wa->height;
c->oldbw = wa->border_width;
if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw)
c->x = c->mon->mx + c->mon->mw - WIDTH(c);
@ -1060,12 +1046,12 @@ manage(Window w, XWindowAttributes *wa)
c->x = MAX(c->x, c->mon->mx);
/* only fix client y-offset, if the client center might cover the bar */
c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx)
&& (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my);
&& (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my);
c->bw = borderpx;
wc.border_width = c->bw;
XConfigureWindow(dpy, w, CWBorderWidth, &wc);
XSetWindowBorder(dpy, w, scheme[SchemeNorm].border->pix);
XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel);
configure(c); /* propagates border_width, if size doesn't change */
updatewindowtype(c);
updatesizehints(c);
@ -1079,7 +1065,7 @@ manage(Window w, XWindowAttributes *wa)
attach(c);
attachstack(c);
XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend,
(unsigned char *) &(c->win), 1);
(unsigned char *) &(c->win), 1);
XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */
setclientstate(c, NormalState);
if (c->mon == selmon)
@ -1163,7 +1149,7 @@ movemouse(const Arg *arg)
ocx = c->x;
ocy = c->y;
if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess)
None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess)
return;
if (!getrootptr(&x, &y))
return;
@ -1182,20 +1168,17 @@ movemouse(const Arg *arg)
nx = ocx + (ev.xmotion.x - x);
ny = ocy + (ev.xmotion.y - y);
if (nx >= selmon->wx && nx <= selmon->wx + selmon->ww
&& ny >= selmon->wy && ny <= selmon->wy + selmon->wh) {
if (abs(selmon->wx - nx) < snap)
nx = selmon->wx;
else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap)
nx = selmon->wx + selmon->ww - WIDTH(c);
if (abs(selmon->wy - ny) < snap)
ny = selmon->wy;
else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap)
ny = selmon->wy + selmon->wh - HEIGHT(c);
if (!c->isfloating && selmon->lt[selmon->sellt]->arrange
&& (abs(nx - c->x) > snap || abs(ny - c->y) > snap))
togglefloating(NULL);
}
if (abs(selmon->wx - nx) < snap)
nx = selmon->wx;
else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap)
nx = selmon->wx + selmon->ww - WIDTH(c);
if (abs(selmon->wy - ny) < snap)
ny = selmon->wy;
else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap)
ny = selmon->wy + selmon->wh - HEIGHT(c);
if (!c->isfloating && selmon->lt[selmon->sellt]->arrange
&& (abs(nx - c->x) > snap || abs(ny - c->y) > snap))
togglefloating(NULL);
if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)
resize(c, nx, ny, c->w, c->h, 1);
break;
@ -1241,7 +1224,7 @@ propertynotify(XEvent *e)
default: break;
case XA_WM_TRANSIENT_FOR:
if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) &&
(c->isfloating = (wintoclient(trans)) != NULL))
(c->isfloating = (wintoclient(trans)) != NULL))
arrange(c->mon);
break;
case XA_WM_NORMAL_HINTS:
@ -1321,7 +1304,7 @@ resizemouse(const Arg *arg)
ocx = c->x;
ocy = c->y;
if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess)
None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess)
return;
XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
do {
@ -1446,7 +1429,7 @@ setclientstate(Client *c, long state)
long data[] = { state, None };
XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
PropModeReplace, (unsigned char *)data, 2);
PropModeReplace, (unsigned char *)data, 2);
}
int
@ -1480,8 +1463,8 @@ setfocus(Client *c)
if (!c->neverfocus) {
XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
XChangeProperty(dpy, root, netatom[NetActiveWindow],
XA_WINDOW, 32, PropModeReplace,
(unsigned char *) &(c->win), 1);
XA_WINDOW, 32, PropModeReplace,
(unsigned char *) &(c->win), 1);
}
sendevent(c, wmatom[WMTakeFocus]);
}
@ -1491,7 +1474,7 @@ setfullscreen(Client *c, int fullscreen)
{
if (fullscreen && !c->isfullscreen) {
XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1);
PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1);
c->isfullscreen = 1;
c->oldstate = c->isfloating;
c->oldbw = c->bw;
@ -1501,7 +1484,7 @@ setfullscreen(Client *c, int fullscreen)
XRaiseWindow(dpy, c->win);
} else if (!fullscreen && c->isfullscreen){
XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
PropModeReplace, (unsigned char*)0, 0);
PropModeReplace, (unsigned char*)0, 0);
c->isfullscreen = 0;
c->isfloating = c->oldstate;
c->bw = c->oldbw;
@ -1528,7 +1511,7 @@ setlayout(const Arg *arg)
drawbar(selmon);
}
/* arg > 1.0 will set mfact absolutly */
/* arg > 1.0 will set mfact absolutely */
void
setmfact(const Arg *arg)
{
@ -1546,7 +1529,9 @@ setmfact(const Arg *arg)
void
setup(void)
{
int i;
XSetWindowAttributes wa;
Atom utf8string;
/* clean up any zombies immediately */
sigchld(0);
@ -1557,12 +1542,13 @@ setup(void)
sh = DisplayHeight(dpy, screen);
root = RootWindow(dpy, screen);
drw = drw_create(dpy, screen, root, sw, sh);
drw_load_fonts(drw, fonts, LENGTH(fonts));
if (!drw->fontcount)
die("no fonts could be loaded.\n");
bh = drw->fonts[0]->h + 2;
if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
die("no fonts could be loaded.");
lrpad = drw->fonts->h;
bh = drw->fonts->h + 2;
updategeom();
/* init atoms */
utf8string = XInternAtom(dpy, "UTF8_STRING", False);
wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
@ -1571,6 +1557,7 @@ setup(void)
netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", 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);
netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
@ -1580,29 +1567,49 @@ setup(void)
cursor[CurResize] = drw_cur_create(drw, XC_sizing);
cursor[CurMove] = drw_cur_create(drw, XC_fleur);
/* init appearance */
scheme[SchemeNorm].border = drw_clr_create(drw, normbordercolor);
scheme[SchemeNorm].bg = drw_clr_create(drw, normbgcolor);
scheme[SchemeNorm].fg = drw_clr_create(drw, normfgcolor);
scheme[SchemeSel].border = drw_clr_create(drw, selbordercolor);
scheme[SchemeSel].bg = drw_clr_create(drw, selbgcolor);
scheme[SchemeSel].fg = drw_clr_create(drw, selfgcolor);
scheme = ecalloc(LENGTH(colors), sizeof(Clr *));
for (i = 0; i < LENGTH(colors); i++)
scheme[i] = drw_scm_create(drw, colors[i], 3);
/* init bars */
updatebars();
updatestatus();
/* supporting window for NetWMCheck */
wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0);
XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32,
PropModeReplace, (unsigned char *) &wmcheckwin, 1);
XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8,
PropModeReplace, (unsigned char *) "dwm", 3);
XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32,
PropModeReplace, (unsigned char *) &wmcheckwin, 1);
/* EWMH support per view */
XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
PropModeReplace, (unsigned char *) netatom, NetLast);
PropModeReplace, (unsigned char *) netatom, NetLast);
XDeleteProperty(dpy, root, netatom[NetClientList]);
/* select for events */
/* select events */
wa.cursor = cursor[CurNormal]->cursor;
wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask|ButtonPressMask|PointerMotionMask
|EnterWindowMask|LeaveWindowMask|StructureNotifyMask|PropertyChangeMask;
wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask
|ButtonPressMask|PointerMotionMask|EnterWindowMask
|LeaveWindowMask|StructureNotifyMask|PropertyChangeMask;
XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa);
XSelectInput(dpy, root, wa.event_mask);
grabkeys();
focus(NULL);
}
void
seturgent(Client *c, int urg)
{
XWMHints *wmh;
c->isurgent = urg;
if (!(wmh = XGetWMHints(dpy, c->win)))
return;
wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint);
XSetWMHints(dpy, c->win, wmh);
XFree(wmh);
}
void
showhide(Client *c)
{
@ -1708,7 +1715,7 @@ togglefloating(const Arg *arg)
selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed;
if (selmon->sel->isfloating)
resize(selmon->sel, selmon->sel->x, selmon->sel->y,
selmon->sel->w, selmon->sel->h, 0);
selmon->sel->w, selmon->sel->h, 0);
arrange(selmon);
}
@ -1745,7 +1752,7 @@ unfocus(Client *c, int setfocus)
if (!c)
return;
grabbuttons(c, 0);
XSetWindowBorder(dpy, c->win, scheme[SchemeNorm].border->pix);
XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel);
if (setfocus) {
XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
@ -1758,12 +1765,11 @@ unmanage(Client *c, int destroyed)
Monitor *m = c->mon;
XWindowChanges wc;
/* The server grab construct avoids race conditions. */
detach(c);
detachstack(c);
if (!destroyed) {
wc.border_width = c->oldbw;
XGrabServer(dpy);
XGrabServer(dpy); /* avoid race conditions */
XSetErrorHandler(xerrordummy);
XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */
XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
@ -1801,14 +1807,16 @@ updatebars(void)
.background_pixmap = ParentRelative,
.event_mask = ButtonPressMask|ExposureMask
};
XClassHint ch = {"dwm", "dwm"};
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),
CopyFromParent, DefaultVisual(dpy, screen),
CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
CopyFromParent, DefaultVisual(dpy, screen),
CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
XMapRaised(dpy, m->barwin);
XSetClassHint(dpy, m->barwin, &ch);
}
}
@ -1835,8 +1843,8 @@ updateclientlist()
for (m = mons; m; m = m->next)
for (c = m->clients; c; c = c->next)
XChangeProperty(dpy, root, netatom[NetClientList],
XA_WINDOW, 32, PropModeAppend,
(unsigned char *) &(c->win), 1);
XA_WINDOW, 32, PropModeAppend,
(unsigned char *) &(c->win), 1);
}
int
@ -1860,8 +1868,8 @@ updategeom(void)
memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo));
XFree(info);
nn = j;
if (n <= nn) {
for (i = 0; i < (nn - n); i++) { /* new monitors available */
if (n <= nn) { /* new monitors available */
for (i = 0; i < (nn - n); i++) {
for (m = mons; m && m->next; m = m->next);
if (m)
m->next = createmon();
@ -1870,8 +1878,8 @@ updategeom(void)
}
for (i = 0, m = mons; i < nn && m; m = m->next, i++)
if (i >= n
|| (unique[i].x_org != m->mx || unique[i].y_org != m->my
|| unique[i].width != m->mw || unique[i].height != m->mh))
|| unique[i].x_org != m->mx || unique[i].y_org != m->my
|| unique[i].width != m->mw || unique[i].height != m->mh)
{
dirty = 1;
m->num = i;
@ -1881,13 +1889,11 @@ updategeom(void)
m->mh = m->wh = unique[i].height;
updatebarpos(m);
}
} else {
/* less monitors available nn < n */
} else { /* less monitors available nn < n */
for (i = nn; i < n; i++) {
for (m = mons; m && m->next; m = m->next);
while (m->clients) {
while ((c = m->clients)) {
dirty = 1;
c = m->clients;
m->clients = c->next;
detachstack(c);
c->mon = mons;
@ -1902,8 +1908,7 @@ updategeom(void)
free(unique);
} else
#endif /* XINERAMA */
/* default monitor setup */
{
{ /* default monitor setup */
if (!mons)
mons = createmon();
if (mons->mw != sw || mons->mh != sh) {
@ -1931,7 +1936,7 @@ updatenumlockmask(void)
for (i = 0; i < 8; i++)
for (j = 0; j < modmap->max_keypermod; j++)
if (modmap->modifiermap[i * modmap->max_keypermod + j]
== XKeysymToKeycode(dpy, XK_Num_Lock))
== XKeysymToKeycode(dpy, XK_Num_Lock))
numlockmask = (1 << i);
XFreeModifiermap(modmap);
}
@ -1976,8 +1981,15 @@ updatesizehints(Client *c)
c->maxa = (float)size.max_aspect.x / size.max_aspect.y;
} else
c->maxa = c->mina = 0.0;
c->isfixed = (c->maxw && c->minw && c->maxh && c->minh
&& c->maxw == c->minw && c->maxh == c->minh);
c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh);
}
void
updatestatus(void)
{
if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
strcpy(stext, "dwm-"VERSION);
drawbar(selmon);
}
void
@ -1989,14 +2001,6 @@ updatetitle(Client *c)
strcpy(c->name, broken);
}
void
updatestatus(void)
{
if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
strcpy(stext, "dwm-"VERSION);
drawbar(selmon);
}
void
updatewindowtype(Client *c)
{
@ -2071,8 +2075,8 @@ wintomon(Window w)
}
/* There's no way to check accesses to destroyed windows, thus those cases are
* ignored (especially on UnmapNotify's). Other types of errors call Xlibs
* default error handler, which may call exit. */
* ignored (especially on UnmapNotify's). Other types of errors call Xlibs
* default error handler, which may call exit. */
int
xerror(Display *dpy, XErrorEvent *ee)
{
@ -2087,7 +2091,7 @@ xerror(Display *dpy, XErrorEvent *ee)
|| (ee->request_code == X_CopyArea && ee->error_code == BadDrawable))
return 0;
fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n",
ee->request_code, ee->error_code);
ee->request_code, ee->error_code);
return xerrorxlib(dpy, ee); /* may call exit */
}
@ -2102,7 +2106,7 @@ xerrordummy(Display *dpy, XErrorEvent *ee)
int
xerrorstart(Display *dpy, XErrorEvent *ee)
{
die("dwm: another window manager is already running\n");
die("dwm: another window manager is already running");
return -1;
}
@ -2124,15 +2128,19 @@ int
main(int argc, char *argv[])
{
if (argc == 2 && !strcmp("-v", argv[1]))
die("dwm-"VERSION "\n");
die("dwm-"VERSION);
else if (argc != 1)
die("usage: dwm [-v]\n");
die("usage: dwm [-v]");
if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
fputs("warning: no locale support\n", stderr);
if (!(dpy = XOpenDisplay(NULL)))
die("dwm: cannot open display\n");
die("dwm: cannot open display");
checkotherwm();
setup();
#ifdef __OpenBSD__
if (pledge("stdio rpath proc exec", NULL) == -1)
die("pledge");
#endif /* __OpenBSD__ */
scan();
run();
cleanup();

4
util.c
View File

@ -12,7 +12,7 @@ ecalloc(size_t nmemb, size_t size)
void *p;
if (!(p = calloc(nmemb, size)))
perror(NULL);
die("calloc:");
return p;
}
@ -27,6 +27,8 @@ die(const char *fmt, ...) {
if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
fputc(' ', stderr);
perror(NULL);
} else {
fputc('\n', stderr);
}
exit(1);

4
util.h
View File

@ -4,5 +4,5 @@
#define MIN(A, B) ((A) < (B) ? (A) : (B))
#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B))
void die(const char *errstr, ...);
void *ecalloc(size_t, size_t);
void die(const char *fmt, ...);
void *ecalloc(size_t nmemb, size_t size);