Compare commits

..

No commits in common. "master" and "6.1" have entirely different histories.
master ... 6.1

13 changed files with 608 additions and 613 deletions

44
BUGS Normal file
View File

@ -0,0 +1,44 @@
---
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.
---

19
LICENSE
View File

@ -1,23 +1,18 @@
MIT/X Consortium License MIT/X Consortium License
© 2006-2019 Anselm R Garbe <anselm@garbe.ca> © 2006-2014 Anselm R Garbe <anselm@garbe.us>
© 2006-2009 Jukka Salmi <jukka at salmi dot ch> © 2010-2014 Hiltjo Posthuma <hiltjo@codemadness.org>
© 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
© 2007-2011 Peter Hartlich <sgkkr at hartlich dot com> © 2007-2011 Peter Hartlich <sgkkr at hartlich dot com>
© 2010-2011 Connor Lane Smith <cls@lubutu.com>
© 2006-2009 Jukka Salmi <jukka at salmi dot ch>
© 2007-2009 Premysl Hruby <dfenze at gmail dot com>
© 2007-2009 Szabolcs Nagy <nszabolcs at gmail dot com> © 2007-2009 Szabolcs Nagy <nszabolcs at gmail dot com>
© 2007-2009 Christof Musik <christof at sendfax dot de> © 2007-2009 Christof Musik <christof at sendfax dot de>
© 2007-2009 Premysl Hruby <dfenze at gmail dot com> © 2009 Mate Nagy <mnagy at port70 dot net>
© 2007-2008 Enno Gottox Boland <gottox at s01 dot de> © 2007-2008 Enno Gottox Boland <gottox at s01 dot de>
© 2008 Martin Hurton <martin dot hurton at gmail dot com> © 2008 Martin Hurton <martin dot hurton at gmail dot com>
© 2008 Neale Pickett <neale dot woozle dot org> © 2008 Neale Pickett <neale dot woozle dot org>
© 2009 Mate Nagy <mnagy at port70 dot net> © 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
© 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>
© 2020-2022 Chris Down <chris@chrisdown.name>
Permission is hereby granted, free of charge, to any person obtaining a Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"), copy of this software and associated documentation files (the "Software"),

View File

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

3
README
View File

@ -18,6 +18,9 @@ necessary as root):
make clean install 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 Running dwm
----------- -----------

4
TODO Normal file
View File

@ -0,0 +1,4 @@
- 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,22 +1,20 @@
/* See LICENSE file for copyright and license details. */ /* See LICENSE file for copyright and license details. */
/* appearance */ /* 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 borderpx = 1; /* border pixel of windows */
static const unsigned int snap = 32; /* snap pixel */ static const unsigned int snap = 32; /* snap pixel */
static const int showbar = 1; /* 0 means no bar */ static const int showbar = 1; /* 0 means no bar */
static const int topbar = 1; /* 0 means bottom 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 */ /* tagging */
static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
@ -35,7 +33,6 @@ static const Rule rules[] = {
static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */
static const int nmaster = 1; /* number of clients in master area */ static const int nmaster = 1; /* number of clients in master area */
static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */
static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */
static const Layout layouts[] = { static const Layout layouts[] = {
/* symbol arrange function */ /* symbol arrange function */
@ -57,10 +54,10 @@ static const Layout layouts[] = {
/* commands */ /* commands */
static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */ static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */
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 *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", normbgcolor, "-nf", normfgcolor, "-sb", selbgcolor, "-sf", selfgcolor, NULL };
static const char *termcmd[] = { "st", NULL }; static const char *termcmd[] = { "st", NULL };
static const Key keys[] = { static Key keys[] = {
/* modifier key function argument */ /* modifier key function argument */
{ MODKEY, XK_p, spawn, {.v = dmenucmd } }, { MODKEY, XK_p, spawn, {.v = dmenucmd } },
{ MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } },
@ -98,8 +95,8 @@ static const Key keys[] = {
}; };
/* button definitions */ /* button definitions */
/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ /* click can be ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
static const Button buttons[] = { static Button buttons[] = {
/* click event mask button function argument */ /* click event mask button function argument */
{ ClkLtSymbol, 0, Button1, setlayout, {0} }, { ClkLtSymbol, 0, Button1, setlayout, {0} },
{ ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} },

View File

@ -1,5 +1,5 @@
# dwm version # dwm version
VERSION = 6.5 VERSION = 6.1
# Customize below to fit your system # Customize below to fit your system
@ -19,17 +19,16 @@ FREETYPELIBS = -lfontconfig -lXft
FREETYPEINC = /usr/include/freetype2 FREETYPEINC = /usr/include/freetype2
# OpenBSD (uncomment) # OpenBSD (uncomment)
#FREETYPEINC = ${X11INC}/freetype2 #FREETYPEINC = ${X11INC}/freetype2
#MANPREFIX = ${PREFIX}/man
# includes and libs # includes and libs
INCS = -I${X11INC} -I${FREETYPEINC} INCS = -I${X11INC} -I${FREETYPEINC}
LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS}
# flags # flags
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} CPPFLAGS = -D_BSD_SOURCE -D_POSIX_C_SOURCE=2 -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
#CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} #CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS}
CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS}
LDFLAGS = ${LIBS} LDFLAGS = -s ${LIBS}
# Solaris # Solaris
#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" #CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\"

410
drw.c
View File

@ -9,48 +9,63 @@
#include "util.h" #include "util.h"
#define UTF_INVALID 0xFFFD #define UTF_INVALID 0xFFFD
#define UTF_SIZ 4
static int static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
utf8decode(const char *s_in, long *u, int *err) static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000};
static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
static long
utf8decodebyte(const char c, size_t *i)
{ {
static const unsigned char lens[] = { for (*i = 0; *i < (UTF_SIZ + 1); ++(*i))
/* 0XXXX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, if (((unsigned char)c & utfmask[*i]) == utfbyte[*i])
/* 10XXX */ 0, 0, 0, 0, 0, 0, 0, 0, /* invalid */ return (unsigned char)c & ~utfmask[*i];
/* 110XX */ 2, 2, 2, 2, return 0;
/* 1110X */ 3, 3,
/* 11110 */ 4,
/* 11111 */ 0, /* invalid */
};
static const unsigned char leading_mask[] = { 0x7F, 0x1F, 0x0F, 0x07 };
static const unsigned int overlong[] = { 0x0, 0x80, 0x0800, 0x10000 };
const unsigned char *s = (const unsigned char *)s_in;
int len = lens[*s >> 3];
*u = UTF_INVALID;
*err = 1;
if (len == 0)
return 1;
long cp = s[0] & leading_mask[len - 1];
for (int i = 1; i < len; ++i) {
if (s[i] == '\0' || (s[i] & 0xC0) != 0x80)
return i;
cp = (cp << 6) | (s[i] & 0x3F);
} }
/* out of range, surrogate, overlong encoding */
if (cp > 0x10FFFF || (cp >> 11) == 0x1B || cp < overlong[len - 1])
return len;
*err = 0; static size_t
*u = cp; utf8validate(long *u, size_t i)
{
if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
*u = UTF_INVALID;
for (i = 1; *u > utfmax[i]; ++i)
;
return i;
}
static size_t
utf8decode(const char *c, long *u, size_t clen)
{
size_t i, j, len, type;
long udecoded;
*u = UTF_INVALID;
if (!clen)
return 0;
udecoded = utf8decodebyte(c[0], &len);
if (!BETWEEN(len, 1, UTF_SIZ))
return 1;
for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
if (type)
return j;
}
if (j < len)
return 0;
*u = udecoded;
utf8validate(u, len);
return len; return len;
} }
Drw * Drw *
drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h)
{ {
Drw *drw = ecalloc(1, sizeof(Drw)); Drw *drw;
drw = ecalloc(1, sizeof(Drw));
drw->dpy = dpy; drw->dpy = dpy;
drw->screen = screen; drw->screen = screen;
drw->root = root; drw->root = root;
@ -58,6 +73,7 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h
drw->h = h; drw->h = h;
drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
drw->gc = XCreateGC(dpy, root, 0, NULL); drw->gc = XCreateGC(dpy, root, 0, NULL);
drw->fontcount = 0;
XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
return drw; return drw;
@ -66,9 +82,6 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h
void void
drw_resize(Drw *drw, unsigned int w, unsigned int h) drw_resize(Drw *drw, unsigned int w, unsigned int h)
{ {
if (!drw)
return;
drw->w = w; drw->w = w;
drw->h = h; drw->h = h;
if (drw->drawable) if (drw->drawable)
@ -79,57 +92,84 @@ drw_resize(Drw *drw, unsigned int w, unsigned int h)
void void
drw_free(Drw *drw) 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); XFreePixmap(drw->dpy, drw->drawable);
XFreeGC(drw->dpy, drw->gc); XFreeGC(drw->dpy, drw->gc);
drw_fontset_free(drw->fonts);
free(drw); free(drw);
} }
/* This function is an implementation detail. Library users should use /* This function is an implementation detail. Library users should use
* drw_fontset_create instead. * drw_font_create instead.
*/ */
static Fnt * static Fnt *
xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) drw_font_xcreate(Drw *drw, const char *fontname, FcPattern *fontpattern)
{ {
Fnt *font; Fnt *font;
XftFont *xfont = NULL; XftFont *xfont = NULL;
FcPattern *pattern = NULL; FcPattern *pattern = NULL;
if (fontname) { if (fontname) {
/* Using the pattern found at font->xfont->pattern does not yield the /* Using the pattern found at font->xfont->pattern does not yield same
* same substitution results as using the pattern returned by * the same substitution results as using the pattern returned by
* FcNameParse; using the latter results in the desired fallback * FcNameParse; using the latter results in the desired fallback
* behaviour whereas the former just results in missing-character * behaviour whereas the former just results in
* rectangles being drawn, at least with some fonts. */ * missing-character-rectangles being drawn, at least with some fonts.
*/
if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) {
fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); fprintf(stderr, "error, cannot load font: '%s'\n", fontname);
return NULL; return NULL;
} }
if (!(pattern = FcNameParse((FcChar8 *) fontname))) { if (!(pattern = FcNameParse((FcChar8 *) fontname))) {
fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); fprintf(stderr, "error, cannot load font: '%s'\n", fontname);
XftFontClose(drw->dpy, xfont); XftFontClose(drw->dpy, xfont);
return NULL; return NULL;
} }
} else if (fontpattern) { } else if (fontpattern) {
if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
fprintf(stderr, "error, cannot load font from pattern.\n"); fprintf(stderr, "error, cannot load font pattern.\n");
return NULL; return NULL;
} }
} else { } else {
die("no font specified."); die("no font specified.\n");
} }
font = ecalloc(1, sizeof(Fnt)); font = ecalloc(1, sizeof(Fnt));
font->xfont = xfont; font->xfont = xfont;
font->pattern = pattern; font->pattern = pattern;
font->h = xfont->ascent + xfont->descent; font->ascent = xfont->ascent;
font->descent = xfont->descent;
font->h = font->ascent + font->descent;
font->dpy = drw->dpy; font->dpy = drw->dpy;
return font; return font;
} }
static void Fnt*
xfont_free(Fnt *font) 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)
{ {
if (!font) if (!font)
return; return;
@ -139,218 +179,150 @@ xfont_free(Fnt *font)
free(font); free(font);
} }
Fnt* Clr *
drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) drw_clr_create(Drw *drw, const char *clrname)
{ {
Fnt *cur, *ret = NULL; Clr *clr;
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), if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen), DefaultColormap(drw->dpy, drw->screen),
clrname, dest)) clrname, &clr->rgb))
die("error, cannot allocate color '%s'", clrname); die("error, cannot allocate color '%s'\n", clrname);
} clr->pix = clr->rgb.pixel;
/* Wrapper to create color schemes. The caller has to call free(3) on the return clr;
* 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 void
drw_setfontset(Drw *drw, Fnt *set) drw_clr_free(Clr *clr)
{ {
if (drw) free(clr);
drw->fonts = set;
} }
void void
drw_setscheme(Drw *drw, Clr *scm) drw_setscheme(Drw *drw, ClrScheme *scheme)
{ {
if (drw) drw->scheme = scheme;
drw->scheme = scm;
} }
void void
drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int empty, int invert)
{ {
if (!drw || !drw->scheme) if (!drw->scheme)
return; return;
XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme->bg->pix : drw->scheme->fg->pix);
if (filled) if (filled)
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w + 1, h + 1);
else else if (empty)
XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
} }
int int
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int invert)
{ {
int ty, ellipsis_x = 0; char buf[1024];
unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, hash, h0, h1; int tx, ty, th;
Extnts tex;
XftDraw *d = NULL; XftDraw *d = NULL;
Fnt *usedfont, *curfont, *nextfont; Fnt *curfont, *nextfont;
int utf8strlen, utf8charlen, utf8err, render = x || y || w || h; size_t i, len;
int utf8strlen, utf8charlen, render;
long utf8codepoint = 0; long utf8codepoint = 0;
const char *utf8str; const char *utf8str;
FcCharSet *fccharset; FcCharSet *fccharset;
FcPattern *fcpattern; FcPattern *fcpattern;
FcPattern *match; FcPattern *match;
XftResult result; XftResult result;
int charexists = 0, overflow = 0; int charexists = 0;
/* keep track of a couple codepoints for which we have no match. */
static unsigned int nomatches[128], ellipsis_width, invalid_width;
static const char invalid[] = "<EFBFBD>";
if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts) if (!drw->scheme || !drw->fontcount)
return 0; return 0;
if (!render) { if (!(render = x || y || w || h)) {
w = invert ? invert : ~invert; w = ~w;
} else { } else {
XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); XSetForeground(drw->dpy, drw->gc, invert ?
drw->scheme->fg->pix : drw->scheme->bg->pix);
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
if (w < lpad)
return x + w;
d = XftDrawCreate(drw->dpy, drw->drawable, d = XftDrawCreate(drw->dpy, drw->drawable,
DefaultVisual(drw->dpy, drw->screen), DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen)); DefaultColormap(drw->dpy, drw->screen));
x += lpad;
w -= lpad;
} }
usedfont = drw->fonts; curfont = drw->fonts[0];
if (!ellipsis_width && render)
ellipsis_width = drw_fontset_getwidth(drw, "...");
if (!invalid_width && render)
invalid_width = drw_fontset_getwidth(drw, invalid);
while (1) { while (1) {
ew = ellipsis_len = utf8err = utf8charlen = utf8strlen = 0; utf8strlen = 0;
utf8str = text; utf8str = text;
nextfont = NULL; nextfont = NULL;
while (*text) { while (*text) {
utf8charlen = utf8decode(text, &utf8codepoint, &utf8err); utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
for (curfont = drw->fonts; curfont; curfont = curfont->next) { for (i = 0; i < drw->fontcount; i++) {
charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); charexists = charexists || XftCharExists(drw->dpy, drw->fonts[i]->xfont, utf8codepoint);
if (charexists) { if (charexists) {
drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL); if (drw->fonts[i] == curfont) {
if (ew + ellipsis_width <= w) { utf8strlen += utf8charlen;
/* 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) {
text += utf8charlen; text += utf8charlen;
utf8strlen += utf8err ? 0 : utf8charlen;
ew += utf8err ? 0 : tmpw;
} else { } else {
nextfont = curfont; nextfont = drw->fonts[i];
} }
break; break;
} }
} }
if (overflow || !charexists || nextfont || utf8err) if (!charexists || (nextfont && nextfont != curfont))
break; break;
else else
charexists = 0; charexists = 0;
} }
if (utf8strlen) { if (utf8strlen) {
if (render) { drw_font_getexts(curfont, utf8str, utf8strlen, &tex);
ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; /* shorten text if necessary */
XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], for (len = MIN(utf8strlen, (sizeof buf) - 1); len && (tex.w > w - drw->fonts[0]->h || w < drw->fonts[0]->h); len--)
usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen); drw_font_getexts(curfont, utf8str, len, &tex);
}
x += ew;
w -= ew;
}
if (utf8err && (!render || invalid_width < w)) {
if (render)
drw_text(drw, x, y, w, h, 0, invalid, invert);
x += invalid_width;
w -= invalid_width;
}
if (render && overflow)
drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert);
if (!*text || overflow) { if (len) {
memcpy(buf, utf8str, len);
buf[len] = '\0';
if (len < utf8strlen)
for (i = len; i && i > len - 3; buf[--i] = '.');
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);
}
x += tex.w;
w -= tex.w;
}
}
if (!*text) {
break; break;
} else if (nextfont) { } else if (nextfont) {
charexists = 0; charexists = 0;
usedfont = nextfont; curfont = nextfont;
} else { } else {
/* Regardless of whether or not a fallback font is found, the /* Regardless of whether or not a fallback font is found, the
* character must be drawn. */ * character must be drawn.
*/
charexists = 1; charexists = 1;
hash = (unsigned int)utf8codepoint; if (drw->fontcount >= DRW_FONT_CACHE_SIZE)
hash = ((hash >> 16) ^ hash) * 0x21F0AAAD; continue;
hash = ((hash >> 15) ^ hash) * 0xD35A2D97;
h0 = ((hash >> 15) ^ hash) % LENGTH(nomatches);
h1 = (hash >> 17) % LENGTH(nomatches);
/* avoid expensive XftFontMatch call when we know we won't find a match */
if (nomatches[h0] == utf8codepoint || nomatches[h1] == utf8codepoint)
goto no_match;
fccharset = FcCharSetCreate(); fccharset = FcCharSetCreate();
FcCharSetAddChar(fccharset, utf8codepoint); FcCharSetAddChar(fccharset, utf8codepoint);
if (!drw->fonts->pattern) { if (!drw->fonts[0]->pattern) {
/* Refer to the comment in xfont_create for more information. */ /* Refer to the comment in drw_font_xcreate for more
die("the first font in the cache must be loaded from a font string."); * information. */
die("the first font in the cache must be loaded from a font string.\n");
} }
fcpattern = FcPatternDuplicate(drw->fonts->pattern); fcpattern = FcPatternDuplicate(drw->fonts[0]->pattern);
FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
@ -362,16 +334,12 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
FcPatternDestroy(fcpattern); FcPatternDestroy(fcpattern);
if (match) { if (match) {
usedfont = xfont_create(drw, NULL, match); curfont = drw_font_xcreate(drw, NULL, match);
if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { if (curfont && XftCharExists(drw->dpy, curfont->xfont, utf8codepoint)) {
for (curfont = drw->fonts; curfont->next; curfont = curfont->next) drw->fonts[drw->fontcount++] = curfont;
; /* NOP */
curfont->next = usedfont;
} else { } else {
xfont_free(usedfont); drw_font_free(curfont);
nomatches[nomatches[h0] ? h1 : h0] = utf8codepoint; curfont = drw->fonts[0];
no_match:
usedfont = drw->fonts;
} }
} }
} }
@ -379,49 +347,34 @@ no_match:
if (d) if (d)
XftDrawDestroy(d); XftDrawDestroy(d);
return x + (render ? w : 0); return x;
} }
void void
drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) 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); XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
XSync(drw->dpy, False); 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);
}
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 void
drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) drw_font_getexts(Fnt *font, const char *text, unsigned int len, Extnts *tex)
{ {
XGlyphInfo ext; XGlyphInfo ext;
if (!font || !text)
return;
XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
if (w) tex->h = font->h;
*w = ext.xOff; tex->w = ext.xOff;
if (h) }
*h = font->h;
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;
} }
Cur * Cur *
@ -429,9 +382,7 @@ drw_cur_create(Drw *drw, int shape)
{ {
Cur *cur; Cur *cur;
if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) cur = ecalloc(1, sizeof(Cur));
return NULL;
cur->cursor = XCreateFontCursor(drw->dpy, shape); cur->cursor = XCreateFontCursor(drw->dpy, shape);
return cur; return cur;
@ -442,7 +393,6 @@ drw_cur_free(Drw *drw, Cur *cursor)
{ {
if (!cursor) if (!cursor)
return; return;
XFreeCursor(drw->dpy, cursor->cursor); XFreeCursor(drw->dpy, cursor->cursor);
free(cursor); free(cursor);
} }

64
drw.h
View File

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

28
dwm.1
View File

@ -10,9 +10,8 @@ and floating layouts. Either layout can be applied dynamically, optimising the
environment for the application in use and the task performed. environment for the application in use and the task performed.
.P .P
In tiled layouts windows are managed in a master and stacking area. The master In tiled layouts windows are managed in a master and stacking area. The master
area on the left contains one window by default, and the stacking area on the area contains the window which currently needs most attention, whereas the
right contains all other windows. The number of master area windows can be stacking area contains all other windows. In monocle layout all windows are
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 maximised to the screen size. In floating layout windows can be resized and
moved freely. Dialog windows are always managed floating, regardless of the moved freely. Dialog windows are always managed floating, regardless of the
layout applied. layout applied.
@ -33,7 +32,7 @@ dwm draws a small border around windows to indicate the focus state.
.SH OPTIONS .SH OPTIONS
.TP .TP
.B \-v .B \-v
prints version information to stderr, then exits. prints version information to standard output, then exits.
.SH USAGE .SH USAGE
.SS Status bar .SS Status bar
.TP .TP
@ -60,11 +59,6 @@ click on a tag label adds/removes that tag to/from the focused window.
Start Start
.BR st(1). .BR st(1).
.TP .TP
.B Mod1\-p
Spawn
.BR dmenu(1)
for launching other programs.
.TP
.B Mod1\-, .B Mod1\-,
Focus previous screen, if any. Focus previous screen, if any.
.TP .TP
@ -99,10 +93,10 @@ Focus next window.
Focus previous window. Focus previous window.
.TP .TP
.B Mod1\-i .B Mod1\-i
Increase number of windows in master area. Increase clients in master area.
.TP .TP
.B Mod1\-d .B Mod1\-d
Decrease number of windows in master area. Decrease clients in master area.
.TP .TP
.B Mod1\-l .B Mod1\-l
Increase master area size. Increase master area size.
@ -158,7 +152,7 @@ code. This keeps it fast, secure and simple.
.SH SEE ALSO .SH SEE ALSO
.BR dmenu (1), .BR dmenu (1),
.BR st (1) .BR st (1)
.SH ISSUES .SH BUGS
Java applications which use the XToolkit/XAWT backend may draw grey windows 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 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 JDK 1.6 versions, because it assumes a reparenting window manager. Possible workarounds
@ -172,5 +166,11 @@ or
(to pretend that a non-reparenting window manager is running that the (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 XToolkit/XAWT backend can recognize) or when using OpenJDK setting the environment variable
.BR _JAVA_AWT_WM_NONREPARENTING=1 . .BR _JAVA_AWT_WM_NONREPARENTING=1 .
.SH BUGS .P
Send all bug reports with a patch to hackers@suckless.org. 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.

371
dwm.c
View File

@ -50,16 +50,17 @@
#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ #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))) * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy)))
#define ISVISIBLE(C) ((C->tags & 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 MOUSEMASK (BUTTONMASK|PointerMotionMask)
#define WIDTH(X) ((X)->w + 2 * (X)->bw) #define WIDTH(X) ((X)->w + 2 * (X)->bw)
#define HEIGHT(X) ((X)->h + 2 * (X)->bw) #define HEIGHT(X) ((X)->h + 2 * (X)->bw)
#define TAGMASK ((1 << LENGTH(tags)) - 1) #define TAGMASK ((1 << LENGTH(tags)) - 1)
#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) #define TEXTW(X) (drw_text(drw, 0, 0, 0, 0, (X), 0) + drw->fonts[0]->h)
/* enums */ /* enums */
enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
enum { SchemeNorm, SchemeSel }; /* color schemes */ enum { SchemeNorm, SchemeSel, SchemeLast }; /* color schemes */
enum { NetSupported, NetWMName, NetWMState, NetWMCheck, enum { NetSupported, NetWMName, NetWMState,
NetWMFullscreen, NetActiveWindow, NetWMWindowType, NetWMFullscreen, NetActiveWindow, NetWMWindowType,
NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
@ -88,7 +89,7 @@ struct Client {
float mina, maxa; float mina, maxa;
int x, y, w, h; int x, y, w, h;
int oldx, oldy, oldw, oldh; int oldx, oldy, oldw, oldh;
int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid; int basew, baseh, incw, inch, maxw, maxh, minw, minh;
int bw, oldbw; int bw, oldbw;
unsigned int tags; unsigned int tags;
int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
@ -151,6 +152,7 @@ static void buttonpress(XEvent *e);
static void checkotherwm(void); static void checkotherwm(void);
static void cleanup(void); static void cleanup(void);
static void cleanupmon(Monitor *mon); static void cleanupmon(Monitor *mon);
static void clearurgent(Client *c);
static void clientmessage(XEvent *e); static void clientmessage(XEvent *e);
static void configure(Client *c); static void configure(Client *c);
static void configurenotify(XEvent *e); static void configurenotify(XEvent *e);
@ -168,7 +170,6 @@ static void focus(Client *c);
static void focusin(XEvent *e); static void focusin(XEvent *e);
static void focusmon(const Arg *arg); static void focusmon(const Arg *arg);
static void focusstack(const Arg *arg); static void focusstack(const Arg *arg);
static Atom getatomprop(Client *c, Atom prop);
static int getrootptr(int *x, int *y); static int getrootptr(int *x, int *y);
static long getstate(Window w); static long getstate(Window w);
static int gettextprop(Window w, Atom atom, char *text, unsigned int size); static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
@ -184,7 +185,7 @@ static void monocle(Monitor *m);
static void motionnotify(XEvent *e); static void motionnotify(XEvent *e);
static void movemouse(const Arg *arg); static void movemouse(const Arg *arg);
static Client *nexttiled(Client *c); static Client *nexttiled(Client *c);
static void pop(Client *c); static void pop(Client *);
static void propertynotify(XEvent *e); static void propertynotify(XEvent *e);
static void quit(const Arg *arg); static void quit(const Arg *arg);
static Monitor *recttomon(int x, int y, int w, int h); static Monitor *recttomon(int x, int y, int w, int h);
@ -202,12 +203,12 @@ static void setfullscreen(Client *c, int fullscreen);
static void setlayout(const Arg *arg); static void setlayout(const Arg *arg);
static void setmfact(const Arg *arg); static void setmfact(const Arg *arg);
static void setup(void); static void setup(void);
static void seturgent(Client *c, int urg);
static void showhide(Client *c); static void showhide(Client *c);
static void sigchld(int unused);
static void spawn(const Arg *arg); static void spawn(const Arg *arg);
static void tag(const Arg *arg); static void tag(const Arg *arg);
static void tagmon(const Arg *arg); static void tagmon(const Arg *arg);
static void tile(Monitor *m); static void tile(Monitor *);
static void togglebar(const Arg *arg); static void togglebar(const Arg *arg);
static void togglefloating(const Arg *arg); static void togglefloating(const Arg *arg);
static void toggletag(const Arg *arg); static void toggletag(const Arg *arg);
@ -215,15 +216,15 @@ static void toggleview(const Arg *arg);
static void unfocus(Client *c, int setfocus); static void unfocus(Client *c, int setfocus);
static void unmanage(Client *c, int destroyed); static void unmanage(Client *c, int destroyed);
static void unmapnotify(XEvent *e); static void unmapnotify(XEvent *e);
static int updategeom(void);
static void updatebarpos(Monitor *m); static void updatebarpos(Monitor *m);
static void updatebars(void); static void updatebars(void);
static void updateclientlist(void); static void updateclientlist(void);
static int updategeom(void);
static void updatenumlockmask(void); static void updatenumlockmask(void);
static void updatesizehints(Client *c); static void updatesizehints(Client *c);
static void updatestatus(void); static void updatestatus(void);
static void updatetitle(Client *c);
static void updatewindowtype(Client *c); static void updatewindowtype(Client *c);
static void updatetitle(Client *c);
static void updatewmhints(Client *c); static void updatewmhints(Client *c);
static void view(const Arg *arg); static void view(const Arg *arg);
static Client *wintoclient(Window w); static Client *wintoclient(Window w);
@ -238,8 +239,7 @@ static const char broken[] = "broken";
static char stext[256]; static char stext[256];
static int screen; static int screen;
static int sw, sh; /* X display screen geometry width, height */ static int sw, sh; /* X display screen geometry width, height */
static int bh; /* bar 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 int (*xerrorxlib)(Display *, XErrorEvent *);
static unsigned int numlockmask = 0; static unsigned int numlockmask = 0;
static void (*handler[LASTEvent]) (XEvent *) = { static void (*handler[LASTEvent]) (XEvent *) = {
@ -261,11 +261,11 @@ static void (*handler[LASTEvent]) (XEvent *) = {
static Atom wmatom[WMLast], netatom[NetLast]; static Atom wmatom[WMLast], netatom[NetLast];
static int running = 1; static int running = 1;
static Cur *cursor[CurLast]; static Cur *cursor[CurLast];
static Clr **scheme; static ClrScheme scheme[SchemeLast];
static Display *dpy; static Display *dpy;
static Drw *drw; static Drw *drw;
static Monitor *mons, *selmon; static Monitor *mons, *selmon;
static Window root, wmcheckwin; static Window root;
/* configuration, allows nested code to access above variables */ /* configuration, allows nested code to access above variables */
#include "config.h" #include "config.h"
@ -343,8 +343,6 @@ applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact)
if (*w < bh) if (*w < bh)
*w = bh; *w = bh;
if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { 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 */ /* see last two sentences in ICCCM 4.1.2.3 */
baseismin = c->basew == c->minw && c->baseh == c->minh; baseismin = c->basew == c->minw && c->baseh == c->minh;
if (!baseismin) { /* temporarily remove base dimensions */ if (!baseismin) { /* temporarily remove base dimensions */
@ -438,16 +436,14 @@ buttonpress(XEvent *e)
if (i < LENGTH(tags)) { if (i < LENGTH(tags)) {
click = ClkTagBar; click = ClkTagBar;
arg.ui = 1 << i; arg.ui = 1 << i;
} else if (ev->x < x + TEXTW(selmon->ltsymbol)) } else if (ev->x < x + blw)
click = ClkLtSymbol; click = ClkLtSymbol;
else if (ev->x > selmon->ww - (int)TEXTW(stext)) else if (ev->x > selmon->ww - TEXTW(stext))
click = ClkStatusText; click = ClkStatusText;
else else
click = ClkWinTitle; click = ClkWinTitle;
} else if ((c = wintoclient(ev->window))) { } else if ((c = wintoclient(ev->window))) {
focus(c); focus(c);
restack(selmon);
XAllowEvents(dpy, ReplayPointer, CurrentTime);
click = ClkClientWin; click = ClkClientWin;
} }
for (i = 0; i < LENGTH(buttons); i++) for (i = 0; i < LENGTH(buttons); i++)
@ -485,10 +481,11 @@ cleanup(void)
cleanupmon(mons); cleanupmon(mons);
for (i = 0; i < CurLast; i++) for (i = 0; i < CurLast; i++)
drw_cur_free(drw, cursor[i]); drw_cur_free(drw, cursor[i]);
for (i = 0; i < LENGTH(colors); i++) for (i = 0; i < SchemeLast; i++) {
free(scheme[i]); drw_clr_free(scheme[i].border);
free(scheme); drw_clr_free(scheme[i].bg);
XDestroyWindow(dpy, wmcheckwin); drw_clr_free(scheme[i].fg);
}
drw_free(drw); drw_free(drw);
XSync(dpy, False); XSync(dpy, False);
XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
@ -511,6 +508,19 @@ cleanupmon(Monitor *mon)
free(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 void
clientmessage(XEvent *e) clientmessage(XEvent *e)
{ {
@ -520,13 +530,15 @@ clientmessage(XEvent *e)
if (!c) if (!c)
return; return;
if (cme->message_type == netatom[NetWMState]) { if (cme->message_type == netatom[NetWMState]) {
if (cme->data.l[1] == netatom[NetWMFullscreen] if (cme->data.l[1] == netatom[NetWMFullscreen] || cme->data.l[2] == netatom[NetWMFullscreen])
|| cme->data.l[2] == netatom[NetWMFullscreen])
setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ 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]) { } else if (cme->message_type == netatom[NetActiveWindow]) {
if (c != selmon->sel && !c->isurgent) if (!ISVISIBLE(c)) {
seturgent(c, 1); c->mon->seltags ^= 1;
c->mon->tagset[c->mon->seltags] = c->tags;
}
pop(c);
} }
} }
@ -553,7 +565,6 @@ void
configurenotify(XEvent *e) configurenotify(XEvent *e)
{ {
Monitor *m; Monitor *m;
Client *c;
XConfigureEvent *ev = &e->xconfigure; XConfigureEvent *ev = &e->xconfigure;
int dirty; int dirty;
@ -565,12 +576,8 @@ configurenotify(XEvent *e)
if (updategeom() || dirty) { if (updategeom() || dirty) {
drw_resize(drw, sw, bh); drw_resize(drw, sw, bh);
updatebars(); 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); XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
}
focus(NULL); focus(NULL);
arrange(NULL); arrange(NULL);
} }
@ -697,21 +704,11 @@ dirtomon(int dir)
void void
drawbar(Monitor *m) drawbar(Monitor *m)
{ {
int x, w, tw = 0; int x, xx, w, dx;
int boxs = drw->fonts->h / 9;
int boxw = drw->fonts->h / 6 + 2;
unsigned int i, occ = 0, urg = 0; unsigned int i, occ = 0, urg = 0;
Client *c; Client *c;
if (!m->showbar) dx = (drw->fonts[0]->ascent + drw->fonts[0]->descent + 2) / 4;
return;
/* draw status first so it can be overdrawn by tags later */
if (m == selmon) { /* status is only drawn on selected monitor */
drw_setscheme(drw, scheme[SchemeNorm]);
tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */
drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0);
}
for (c = m->clients; c; c = c->next) { for (c = m->clients; c; c = c->next) {
occ |= c->tags; occ |= c->tags;
@ -721,27 +718,36 @@ drawbar(Monitor *m)
x = 0; x = 0;
for (i = 0; i < LENGTH(tags); i++) { for (i = 0; i < LENGTH(tags); i++) {
w = TEXTW(tags[i]); w = TEXTW(tags[i]);
drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); drw_setscheme(drw, m->tagset[m->seltags] & 1 << i ? &scheme[SchemeSel] : &scheme[SchemeNorm]);
drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); drw_text(drw, x, 0, w, bh, tags[i], urg & 1 << i);
if (occ & 1 << i) drw_rect(drw, x + 1, 1, dx, dx, m == selmon && selmon->sel && selmon->sel->tags & 1 << i,
drw_rect(drw, x + boxs, boxs, boxw, boxw, occ & 1 << i, urg & 1 << i);
m == selmon && selmon->sel && selmon->sel->tags & 1 << i,
urg & 1 << i);
x += w; x += w;
} }
w = TEXTW(m->ltsymbol); w = blw = TEXTW(m->ltsymbol);
drw_setscheme(drw, scheme[SchemeNorm]); drw_setscheme(drw, &scheme[SchemeNorm]);
x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); drw_text(drw, x, 0, w, bh, m->ltsymbol, 0);
x += w;
if ((w = m->ww - tw - x) > bh) { 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;
if (m->sel) { if (m->sel) {
drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); drw_setscheme(drw, m == selmon ? &scheme[SchemeSel] : &scheme[SchemeNorm]);
drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); drw_text(drw, x, 0, w, bh, m->sel->name, 0);
if (m->sel->isfloating) drw_rect(drw, x + 1, 1, dx, dx, m->sel->isfixed, m->sel->isfloating, 0);
drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0);
} else { } else {
drw_setscheme(drw, scheme[SchemeNorm]); drw_setscheme(drw, &scheme[SchemeNorm]);
drw_rect(drw, x, 0, w, bh, 1, 1); drw_rect(drw, x, 0, w, bh, 1, 0, 1);
} }
} }
drw_map(drw, m->barwin, 0, 0, m->ww, bh); drw_map(drw, m->barwin, 0, 0, m->ww, bh);
@ -790,17 +796,18 @@ focus(Client *c)
{ {
if (!c || !ISVISIBLE(c)) if (!c || !ISVISIBLE(c))
for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext);
/* was if (selmon->sel) */
if (selmon->sel && selmon->sel != c) if (selmon->sel && selmon->sel != c)
unfocus(selmon->sel, 0); unfocus(selmon->sel, 0);
if (c) { if (c) {
if (c->mon != selmon) if (c->mon != selmon)
selmon = c->mon; selmon = c->mon;
if (c->isurgent) if (c->isurgent)
seturgent(c, 0); clearurgent(c);
detachstack(c); detachstack(c);
attachstack(c); attachstack(c);
grabbuttons(c, 1); grabbuttons(c, 1);
XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); XSetWindowBorder(dpy, c->win, scheme[SchemeSel].border->pix);
setfocus(c); setfocus(c);
} else { } else {
XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
@ -810,7 +817,7 @@ focus(Client *c)
drawbars(); drawbars();
} }
/* there are some broken focus acquiring clients needing extra handling */ /* there are some broken focus acquiring clients */
void void
focusin(XEvent *e) focusin(XEvent *e)
{ {
@ -829,7 +836,8 @@ focusmon(const Arg *arg)
return; return;
if ((m = dirtomon(arg->i)) == selmon) if ((m = dirtomon(arg->i)) == selmon)
return; return;
unfocus(selmon->sel, 0); unfocus(selmon->sel, 0); /* s/1/0/ fixes input focus issues
in gedit and anjuta */
selmon = m; selmon = m;
focus(NULL); focus(NULL);
} }
@ -839,7 +847,7 @@ focusstack(const Arg *arg)
{ {
Client *c = NULL, *i; Client *c = NULL, *i;
if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) if (!selmon->sel)
return; return;
if (arg->i > 0) { if (arg->i > 0) {
for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next);
@ -914,14 +922,17 @@ gettextprop(Window w, Atom atom, char *text, unsigned int size)
if (!text || size == 0) if (!text || size == 0)
return 0; return 0;
text[0] = '\0'; text[0] = '\0';
if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) XGetTextProperty(dpy, w, &name, atom);
if (!name.nitems)
return 0; return 0;
if (name.encoding == XA_STRING) { if (name.encoding == XA_STRING)
strncpy(text, (char *)name.value, size - 1); strncpy(text, (char *)name.value, size - 1);
} else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { else {
if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) {
strncpy(text, *list, size - 1); strncpy(text, *list, size - 1);
XFreeStringList(list); XFreeStringList(list);
} }
}
text[size - 1] = '\0'; text[size - 1] = '\0';
XFree(name.value); XFree(name.value);
return 1; return 1;
@ -935,9 +946,7 @@ grabbuttons(Client *c, int focused)
unsigned int i, j; unsigned int i, j;
unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
XUngrabButton(dpy, AnyButton, AnyModifier, c->win); XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
if (!focused) if (focused) {
XGrabButton(dpy, AnyButton, AnyModifier, c->win, False,
BUTTONMASK, GrabModeSync, GrabModeSync, None, None);
for (i = 0; i < LENGTH(buttons); i++) for (i = 0; i < LENGTH(buttons); i++)
if (buttons[i].click == ClkClientWin) if (buttons[i].click == ClkClientWin)
for (j = 0; j < LENGTH(modifiers); j++) for (j = 0; j < LENGTH(modifiers); j++)
@ -945,6 +954,9 @@ grabbuttons(Client *c, int focused)
buttons[i].mask | modifiers[j], buttons[i].mask | modifiers[j],
c->win, False, BUTTONMASK, c->win, False, BUTTONMASK,
GrabModeAsync, GrabModeSync, None, None); GrabModeAsync, GrabModeSync, None, None);
} else
XGrabButton(dpy, AnyButton, AnyModifier, c->win, False,
BUTTONMASK, GrabModeAsync, GrabModeSync, None, None);
} }
} }
@ -953,26 +965,16 @@ grabkeys(void)
{ {
updatenumlockmask(); updatenumlockmask();
{ {
unsigned int i, j, k; unsigned int i, j;
unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
int start, end, skip; KeyCode code;
KeySym *syms;
XUngrabKey(dpy, AnyKey, AnyModifier, root); XUngrabKey(dpy, AnyKey, AnyModifier, root);
XDisplayKeycodes(dpy, &start, &end);
syms = XGetKeyboardMapping(dpy, start, end - start + 1, &skip);
if (!syms)
return;
for (k = start; k <= end; k++)
for (i = 0; i < LENGTH(keys); i++) for (i = 0; i < LENGTH(keys); i++)
/* skip modifier codes, we do that ourselves */ if ((code = XKeysymToKeycode(dpy, keys[i].keysym)))
if (keys[i].keysym == syms[(k - start) * skip])
for (j = 0; j < LENGTH(modifiers); j++) for (j = 0; j < LENGTH(modifiers); j++)
XGrabKey(dpy, k, XGrabKey(dpy, code, keys[i].mod | modifiers[j], root,
keys[i].mod | modifiers[j], True, GrabModeAsync, GrabModeAsync);
root, True,
GrabModeAsync, GrabModeAsync);
XFree(syms);
} }
} }
@ -1036,13 +1038,6 @@ manage(Window w, XWindowAttributes *wa)
c = ecalloc(1, sizeof(Client)); c = ecalloc(1, sizeof(Client));
c->win = w; 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); updatetitle(c);
if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
c->mon = t->mon; c->mon = t->mon;
@ -1051,18 +1046,26 @@ manage(Window w, XWindowAttributes *wa)
c->mon = selmon; c->mon = selmon;
applyrules(c); 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->wx + c->mon->ww) if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw)
c->x = c->mon->wx + c->mon->ww - WIDTH(c); c->x = c->mon->mx + c->mon->mw - WIDTH(c);
if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh) if (c->y + HEIGHT(c) > c->mon->my + c->mon->mh)
c->y = c->mon->wy + c->mon->wh - HEIGHT(c); c->y = c->mon->my + c->mon->mh - HEIGHT(c);
c->x = MAX(c->x, c->mon->wx); c->x = MAX(c->x, c->mon->mx);
c->y = MAX(c->y, c->mon->wy); /* 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->bw = borderpx; c->bw = borderpx;
wc.border_width = c->bw; wc.border_width = c->bw;
XConfigureWindow(dpy, w, CWBorderWidth, &wc); XConfigureWindow(dpy, w, CWBorderWidth, &wc);
XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); XSetWindowBorder(dpy, w, scheme[SchemeNorm].border->pix);
configure(c); /* propagates border_width, if size doesn't change */ configure(c); /* propagates border_width, if size doesn't change */
updatewindowtype(c); updatewindowtype(c);
updatesizehints(c); updatesizehints(c);
@ -1103,7 +1106,9 @@ maprequest(XEvent *e)
static XWindowAttributes wa; static XWindowAttributes wa;
XMapRequestEvent *ev = &e->xmaprequest; XMapRequestEvent *ev = &e->xmaprequest;
if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect) if (!XGetWindowAttributes(dpy, ev->window, &wa))
return;
if (wa.override_redirect)
return; return;
if (!wintoclient(ev->window)) if (!wintoclient(ev->window))
manage(ev->window, &wa); manage(ev->window, &wa);
@ -1177,6 +1182,8 @@ movemouse(const Arg *arg)
nx = ocx + (ev.xmotion.x - x); nx = ocx + (ev.xmotion.x - x);
ny = ocy + (ev.xmotion.y - y); 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) if (abs(selmon->wx - nx) < snap)
nx = selmon->wx; nx = selmon->wx;
else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap)
@ -1188,6 +1195,7 @@ movemouse(const Arg *arg)
if (!c->isfloating && selmon->lt[selmon->sellt]->arrange if (!c->isfloating && selmon->lt[selmon->sellt]->arrange
&& (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) && (abs(nx - c->x) > snap || abs(ny - c->y) > snap))
togglefloating(NULL); togglefloating(NULL);
}
if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)
resize(c, nx, ny, c->w, c->h, 1); resize(c, nx, ny, c->w, c->h, 1);
break; break;
@ -1237,7 +1245,7 @@ propertynotify(XEvent *e)
arrange(c->mon); arrange(c->mon);
break; break;
case XA_WM_NORMAL_HINTS: case XA_WM_NORMAL_HINTS:
c->hintsvalid = 0; updatesizehints(c);
break; break;
case XA_WM_HINTS: case XA_WM_HINTS:
updatewmhints(c); updatewmhints(c);
@ -1520,7 +1528,7 @@ setlayout(const Arg *arg)
drawbar(selmon); drawbar(selmon);
} }
/* arg > 1.0 will set mfact absolutely */ /* arg > 1.0 will set mfact absolutly */
void void
setmfact(const Arg *arg) setmfact(const Arg *arg)
{ {
@ -1529,7 +1537,7 @@ setmfact(const Arg *arg)
if (!arg || !selmon->lt[selmon->sellt]->arrange) if (!arg || !selmon->lt[selmon->sellt]->arrange)
return; return;
f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
if (f < 0.05 || f > 0.95) if (f < 0.1 || f > 0.9)
return; return;
selmon->mfact = f; selmon->mfact = f;
arrange(selmon); arrange(selmon);
@ -1538,19 +1546,10 @@ setmfact(const Arg *arg)
void void
setup(void) setup(void)
{ {
int i;
XSetWindowAttributes wa; XSetWindowAttributes wa;
Atom utf8string;
struct sigaction sa;
/* do not transform children into zombies when they terminate */ /* clean up any zombies immediately */
sigemptyset(&sa.sa_mask); sigchld(0);
sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART;
sa.sa_handler = SIG_IGN;
sigaction(SIGCHLD, &sa, NULL);
/* clean up any zombies (inherited from .xinitrc etc) immediately */
while (waitpid(-1, NULL, WNOHANG) > 0);
/* init screen */ /* init screen */
screen = DefaultScreen(dpy); screen = DefaultScreen(dpy);
@ -1558,13 +1557,12 @@ setup(void)
sh = DisplayHeight(dpy, screen); sh = DisplayHeight(dpy, screen);
root = RootWindow(dpy, screen); root = RootWindow(dpy, screen);
drw = drw_create(dpy, screen, root, sw, sh); drw = drw_create(dpy, screen, root, sw, sh);
if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) drw_load_fonts(drw, fonts, LENGTH(fonts));
die("no fonts could be loaded."); if (!drw->fontcount)
lrpad = drw->fonts->h; die("no fonts could be loaded.\n");
bh = drw->fonts->h + 2; bh = drw->fonts[0]->h + 2;
updategeom(); updategeom();
/* init atoms */ /* init atoms */
utf8string = XInternAtom(dpy, "UTF8_STRING", False);
wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
@ -1573,7 +1571,6 @@ setup(void)
netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", 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[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
@ -1583,48 +1580,29 @@ setup(void)
cursor[CurResize] = drw_cur_create(drw, XC_sizing); cursor[CurResize] = drw_cur_create(drw, XC_sizing);
cursor[CurMove] = drw_cur_create(drw, XC_fleur); cursor[CurMove] = drw_cur_create(drw, XC_fleur);
/* init appearance */ /* init appearance */
scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); scheme[SchemeNorm].border = drw_clr_create(drw, normbordercolor);
for (i = 0; i < LENGTH(colors); i++) scheme[SchemeNorm].bg = drw_clr_create(drw, normbgcolor);
scheme[i] = drw_scm_create(drw, colors[i], 3); 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);
/* init bars */ /* init bars */
updatebars(); updatebars();
updatestatus(); 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 */ /* EWMH support per view */
XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
PropModeReplace, (unsigned char *) netatom, NetLast); PropModeReplace, (unsigned char *) netatom, NetLast);
XDeleteProperty(dpy, root, netatom[NetClientList]); XDeleteProperty(dpy, root, netatom[NetClientList]);
/* select events */ /* select for events */
wa.cursor = cursor[CurNormal]->cursor; wa.cursor = cursor[CurNormal]->cursor;
wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask|ButtonPressMask|PointerMotionMask
|ButtonPressMask|PointerMotionMask|EnterWindowMask |EnterWindowMask|LeaveWindowMask|StructureNotifyMask|PropertyChangeMask;
|LeaveWindowMask|StructureNotifyMask|PropertyChangeMask;
XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa);
XSelectInput(dpy, root, wa.event_mask); XSelectInput(dpy, root, wa.event_mask);
grabkeys(); grabkeys();
focus(NULL); 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 void
showhide(Client *c) showhide(Client *c)
{ {
@ -1643,25 +1621,27 @@ showhide(Client *c)
} }
} }
void
sigchld(int unused)
{
if (signal(SIGCHLD, sigchld) == SIG_ERR)
die("can't install SIGCHLD handler:");
while (0 < waitpid(-1, NULL, WNOHANG));
}
void void
spawn(const Arg *arg) spawn(const Arg *arg)
{ {
struct sigaction sa;
if (arg->v == dmenucmd) if (arg->v == dmenucmd)
dmenumon[0] = '0' + selmon->num; dmenumon[0] = '0' + selmon->num;
if (fork() == 0) { if (fork() == 0) {
if (dpy) if (dpy)
close(ConnectionNumber(dpy)); close(ConnectionNumber(dpy));
setsid(); setsid();
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = SIG_DFL;
sigaction(SIGCHLD, &sa, NULL);
execvp(((char **)arg->v)[0], (char **)arg->v); execvp(((char **)arg->v)[0], (char **)arg->v);
die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]); fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]);
perror(" failed");
exit(EXIT_SUCCESS);
} }
} }
@ -1701,12 +1681,10 @@ tile(Monitor *m)
if (i < m->nmaster) { if (i < m->nmaster) {
h = (m->wh - my) / (MIN(n, m->nmaster) - i); 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), h - (2*c->bw), 0);
if (my + HEIGHT(c) < m->wh)
my += HEIGHT(c); my += HEIGHT(c);
} else { } else {
h = (m->wh - ty) / (n - i); 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); 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); ty += HEIGHT(c);
} }
} }
@ -1767,7 +1745,7 @@ unfocus(Client *c, int setfocus)
if (!c) if (!c)
return; return;
grabbuttons(c, 0); grabbuttons(c, 0);
XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); XSetWindowBorder(dpy, c->win, scheme[SchemeNorm].border->pix);
if (setfocus) { if (setfocus) {
XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
XDeleteProperty(dpy, root, netatom[NetActiveWindow]); XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
@ -1780,13 +1758,13 @@ unmanage(Client *c, int destroyed)
Monitor *m = c->mon; Monitor *m = c->mon;
XWindowChanges wc; XWindowChanges wc;
/* The server grab construct avoids race conditions. */
detach(c); detach(c);
detachstack(c); detachstack(c);
if (!destroyed) { if (!destroyed) {
wc.border_width = c->oldbw; wc.border_width = c->oldbw;
XGrabServer(dpy); /* avoid race conditions */ XGrabServer(dpy);
XSetErrorHandler(xerrordummy); XSetErrorHandler(xerrordummy);
XSelectInput(dpy, c->win, NoEventMask);
XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */
XUngrabButton(dpy, AnyButton, AnyModifier, c->win); XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
setclientstate(c, WithdrawnState); setclientstate(c, WithdrawnState);
@ -1823,7 +1801,6 @@ updatebars(void)
.background_pixmap = ParentRelative, .background_pixmap = ParentRelative,
.event_mask = ButtonPressMask|ExposureMask .event_mask = ButtonPressMask|ExposureMask
}; };
XClassHint ch = {"dwm", "dwm"};
for (m = mons; m; m = m->next) { for (m = mons; m; m = m->next) {
if (m->barwin) if (m->barwin)
continue; continue;
@ -1832,7 +1809,6 @@ updatebars(void)
CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
XMapRaised(dpy, m->barwin); XMapRaised(dpy, m->barwin);
XSetClassHint(dpy, m->barwin, &ch);
} }
} }
@ -1850,7 +1826,7 @@ updatebarpos(Monitor *m)
} }
void void
updateclientlist(void) updateclientlist()
{ {
Client *c; Client *c;
Monitor *m; Monitor *m;
@ -1884,9 +1860,8 @@ updategeom(void)
memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo));
XFree(info); XFree(info);
nn = j; nn = j;
if (n <= nn) {
/* new monitors if nn > n */ for (i = 0; i < (nn - n); i++) { /* new monitors available */
for (i = n; i < nn; i++) {
for (m = mons; m && m->next; m = m->next); for (m = mons; m && m->next; m = m->next);
if (m) if (m)
m->next = createmon(); m->next = createmon();
@ -1895,8 +1870,8 @@ updategeom(void)
} }
for (i = 0, m = mons; i < nn && m; m = m->next, i++) for (i = 0, m = mons; i < nn && m; m = m->next, i++)
if (i >= n if (i >= n
|| unique[i].x_org != m->mx || unique[i].y_org != m->my || (unique[i].x_org != m->mx || unique[i].y_org != m->my
|| unique[i].width != m->mw || unique[i].height != m->mh) || unique[i].width != m->mw || unique[i].height != m->mh))
{ {
dirty = 1; dirty = 1;
m->num = i; m->num = i;
@ -1906,11 +1881,13 @@ updategeom(void)
m->mh = m->wh = unique[i].height; m->mh = m->wh = unique[i].height;
updatebarpos(m); updatebarpos(m);
} }
/* removed monitors if n > nn */ } else {
/* less monitors available nn < n */
for (i = nn; i < n; i++) { for (i = nn; i < n; i++) {
for (m = mons; m && m->next; m = m->next); for (m = mons; m && m->next; m = m->next);
while ((c = m->clients)) { while (m->clients) {
dirty = 1; dirty = 1;
c = m->clients;
m->clients = c->next; m->clients = c->next;
detachstack(c); detachstack(c);
c->mon = mons; c->mon = mons;
@ -1921,10 +1898,12 @@ updategeom(void)
selmon = mons; selmon = mons;
cleanupmon(m); cleanupmon(m);
} }
}
free(unique); free(unique);
} else } else
#endif /* XINERAMA */ #endif /* XINERAMA */
{ /* default monitor setup */ /* default monitor setup */
{
if (!mons) if (!mons)
mons = createmon(); mons = createmon();
if (mons->mw != sw || mons->mh != sh) { if (mons->mw != sw || mons->mh != sh) {
@ -1997,16 +1976,8 @@ updatesizehints(Client *c)
c->maxa = (float)size.max_aspect.x / size.max_aspect.y; c->maxa = (float)size.max_aspect.x / size.max_aspect.y;
} else } else
c->maxa = c->mina = 0.0; c->maxa = c->mina = 0.0;
c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); c->isfixed = (c->maxw && c->minw && c->maxh && c->minh
c->hintsvalid = 1; && 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 void
@ -2018,6 +1989,14 @@ updatetitle(Client *c)
strcpy(c->name, broken); strcpy(c->name, broken);
} }
void
updatestatus(void)
{
if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
strcpy(stext, "dwm-"VERSION);
drawbar(selmon);
}
void void
updatewindowtype(Client *c) updatewindowtype(Client *c)
{ {
@ -2123,7 +2102,7 @@ xerrordummy(Display *dpy, XErrorEvent *ee)
int int
xerrorstart(Display *dpy, XErrorEvent *ee) xerrorstart(Display *dpy, XErrorEvent *ee)
{ {
die("dwm: another window manager is already running"); die("dwm: another window manager is already running\n");
return -1; return -1;
} }
@ -2132,9 +2111,11 @@ zoom(const Arg *arg)
{ {
Client *c = selmon->sel; Client *c = selmon->sel;
if (!selmon->lt[selmon->sellt]->arrange || !c || c->isfloating) if (!selmon->lt[selmon->sellt]->arrange
|| (selmon->sel && selmon->sel->isfloating))
return; return;
if (c == nexttiled(selmon->clients) && !(c = nexttiled(c->next))) if (c == nexttiled(selmon->clients))
if (!c || !(c = nexttiled(c->next)))
return; return;
pop(c); pop(c);
} }
@ -2143,19 +2124,15 @@ int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
if (argc == 2 && !strcmp("-v", argv[1])) if (argc == 2 && !strcmp("-v", argv[1]))
die("dwm-"VERSION); die("dwm-"VERSION "\n");
else if (argc != 1) else if (argc != 1)
die("usage: dwm [-v]"); die("usage: dwm [-v]\n");
if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
fputs("warning: no locale support\n", stderr); fputs("warning: no locale support\n", stderr);
if (!(dpy = XOpenDisplay(NULL))) if (!(dpy = XOpenDisplay(NULL)))
die("dwm: cannot open display"); die("dwm: cannot open display\n");
checkotherwm(); checkotherwm();
setup(); setup();
#ifdef __OpenBSD__
if (pledge("stdio rpath proc exec", NULL) == -1)
die("pledge");
#endif /* __OpenBSD__ */
scan(); scan();
run(); run();
cleanup(); cleanup();

38
util.c
View File

@ -1,5 +1,4 @@
/* See LICENSE file for copyright and license details. */ /* See LICENSE file for copyright and license details. */
#include <errno.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -7,31 +6,28 @@
#include "util.h" #include "util.h"
void
die(const char *fmt, ...)
{
va_list ap;
int saved_errno;
saved_errno = errno;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
if (fmt[0] && fmt[strlen(fmt)-1] == ':')
fprintf(stderr, " %s", strerror(saved_errno));
fputc('\n', stderr);
exit(1);
}
void * void *
ecalloc(size_t nmemb, size_t size) ecalloc(size_t nmemb, size_t size)
{ {
void *p; void *p;
if (!(p = calloc(nmemb, size))) if (!(p = calloc(nmemb, size)))
die("calloc:"); perror(NULL);
return p; return p;
} }
void
die(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
fputc(' ', stderr);
perror(NULL);
}
exit(1);
}

5
util.h
View File

@ -3,7 +3,6 @@
#define MAX(A, B) ((A) > (B) ? (A) : (B)) #define MAX(A, B) ((A) > (B) ? (A) : (B))
#define MIN(A, B) ((A) < (B) ? (A) : (B)) #define MIN(A, B) ((A) < (B) ? (A) : (B))
#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) #define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B))
#define LENGTH(X) (sizeof (X) / sizeof (X)[0])
void die(const char *fmt, ...); void die(const char *errstr, ...);
void *ecalloc(size_t nmemb, size_t size); void *ecalloc(size_t, size_t);