Compare commits

...

26 Commits
6.4 ... master

Author SHA1 Message Date
1bae806fe5 fix: comment out main, so there is only one main signature.
dwm.c also has a main function defined.
2026-01-25 12:46:27 +01:00
2f76a5cc49 fix: forgot to include half of the libraries. 2026-01-25 12:46:04 +01:00
ab2253a0ee Ignore compile_commands.json 2026-01-25 12:39:49 +01:00
c23f9dfe2e Move config.mk into Makefile, and delete config.mk.
config.mk shall now contain user-specific configurations, and thus is
now ignored.
2026-01-25 12:39:49 +01:00
eda3495acf fix: change = to := 2026-01-25 12:37:29 +01:00
1448916be3 Rework makefile and worktree.
Moved most source files to /src/
Renamed `LICENSE` to `COPYING` to reduce (my personal) misspellings.
Rewrote Makefile to be more Quinn-friendly, and removed some components
not necessary for me, since this is a personal project.
2026-01-25 12:20:47 +01:00
f12825443e Specify /bin specifically, as opposed to any directory called bin/ 2026-01-25 09:23:53 +01:00
c750f87edd Add .gitignore 2026-01-19 19:12:47 +01:00
Chris Down
a9aa0d8ffb dwm: Fix getatomprop regression from heap overflow fix
Commit 244fa852fe ("dwm: Fix heap buffer overflow in getatomprop")
introduced a check for dl > 0 before dereferencing the property pointer.
However, I missed that the variable dl is passed to XGetWindowProperty
for both nitems_return and bytes_after_return parameters:

    XGetWindowProperty(..., &dl, &dl, &p)

The final value in dl is bytes_after_return, not nitems_return. For a
successfully read property, bytes_after is typically 0 (indicating all
data was retrieved), so the check `dl > 0` is always false and dwm never
reads any atom properties. So this is safe, but not very helpful :-)

dl is probably just a dummy variable anyway, so fix by using a separate
variable for nitems, and check nitems > 0 as originally intended.
2026-01-16 14:13:51 +01:00
Hiltjo Posthuma
85fe518c1a bump version to 6.7
Put the maintainer at the top and bump years (time flies).
2026-01-10 11:31:44 +01:00
Chris Down
244fa852fe dwm: Fix heap buffer overflow in getatomprop
When getatomprop() is called, it invokes XGetWindowProperty() to
retrieve an Atom. If the property exists but has zero elements (length
0), Xlib returns Success and sets p to a valid, non-NULL memory address
containing a single null byte.

However, dl (that is, the number of items) is 0. dwm blindly casts p to
Atom* and dereferences it. While Xlib guarantees that p is safe to read
as a string (that is, it is null-terminated), it does _not_ guarantee it
is safe to read as an Atom (an unsigned long).

The Atom type is a typedef for unsigned long. Reading an Atom (which
thus will either likely be 4 or 8 bytes) from a 1-byte allocated buffer
results in a heap buffer overflow. Since property content is user
controlled, this allows any client to trigger an out of bounds read
simply by setting a property with format 32 and length 0.

An example client which reliably crashes dwm under ASAN:

    #include <X11/Xlib.h>
    #include <X11/Xatom.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>

    int main(void) {
        Display *d;
        Window root, w;
        Atom net_wm_state;

        d = XOpenDisplay(NULL);
        if (!d) return 1;

        root = DefaultRootWindow(d);
        w = XCreateSimpleWindow(d, root, 10, 10, 200, 200, 1, 0, 0);
        net_wm_state = XInternAtom(d, "_NET_WM_STATE", False);
        if (net_wm_state == None) return 1;

        XChangeProperty(d, w, net_wm_state, XA_ATOM, 32,
                        PropModeReplace, NULL, 0);
        XMapWindow(d, w);
        XSync(d, False);
        sleep(1);

        XCloseDisplay(d);
        return 0;
    }

In order to avoid this, check that the number of items returned is
greater than zero before dereferencing the pointer.
2026-01-10 11:27:23 +01:00
Hiltjo Posthuma
7c3abae4e6 drw.c: drw_scm_free: call free inside
Because drw_scm_create() allocates it.
2025-09-29 18:48:27 +02:00
Hiltjo Posthuma
93f26863d1 cleanup schemes and colors 2025-09-27 12:10:17 +02:00
Hiltjo Posthuma
74edc27caa config: make refreshrate for mouse move/resize a config option
Bump the default from 60 to 120.
2025-08-12 19:17:20 +02:00
Hiltjo Posthuma
693d94d350 bump version to 6.6 2025-08-09 14:34:03 +02:00
Raymond Cole
cfb8627a80 Avoid unsigned integer underflow in drw_text() 2024-10-30 13:02:17 +01:00
Hiltjo Posthuma
fcb2476b69 util.c: output function might override errno and thus affect perror()
Original patch by Raymond Cole with some modifications, thanks!
2024-10-27 20:10:07 +01:00
Hiltjo Posthuma
8933ebcf50 sync drw.{c,h} from dmenu
- drw: minor improvement to the nomatches cache
- overhaul utf8decoding and render invalid utf8 sequences as U+FFFD.

Thanks NRK for these improvements!
2024-10-05 13:06:08 +02:00
Pontus Stenetorp
5687f46964 Add missing void to updateclientlist definition
Caught by -pedantic implying -Wstrict-prototypes for OpenBSD's 16.0.6 Clang.
2024-06-08 18:21:00 +02:00
Hiltjo Posthuma
061e9fe9a7 bump version to 6.5 2024-03-19 12:13:16 +01:00
Hiltjo Posthuma
9f8855343c Makefile: remove the options target
The Makefile used to suppress output (by using @), so this target made sense at
the time.

But the Makefile should be simple and make debugging with less abstractions or
fancy printing.  The Makefile was made verbose and doesn't hide the build
output, so remove this target.

Prompted by a question on the mailing list about the options target.
2023-09-22 15:13:29 +02:00
Hiltjo Posthuma
e81f17d4c1 restore SIGCHLD sighandler to default before spawning a program
From sigaction(2):
A child created via fork(2) inherits a copy of its parent's signal dispositions.
During an execve(2), the dispositions of handled signals are reset to the default;
the dispositions of ignored signals are left unchanged.

This refused to start directly some programs from configuring in config.h:

static Key keys[] = {
	MODKEY,                       XK_o,      spawn,          {.v = cmd } },
};

Some reported programs that didn't start were: mpv, anki, dmenu_extended.

Reported by pfx.
Initial patch suggestion by Storkman.
2023-04-09 12:37:14 +02:00
NRK
348f6559ab config.mk: update to _XOPEN_SOURCE=700L
SA_NOCLDWAIT is marked as XSI in the posix spec [0] and FreeBSD and NetBSD
seems to more be strict about the feature test macro [1].

so update the macro to use _XOPEN_SOURCE=700L instead, which is equivalent to
_POSIX_C_SOURCE=200809L except that it also unlocks the X/Open System
Interfaces.

[0]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html#tag_13_42
[1]: https://lists.suckless.org/dev/2302/35111.html

Tested on:
* NetBSD 9.3 (fixed).
* FreeBSD 13 (fixed).
* Void Linux musl.
* Void Linux glibc.
* OpenBSD 7.2 (stable).
* Slackware 11.

Reported-by: beastie <pufferfish@riseup.net>
2023-02-17 15:27:34 +01:00
Chris Down
712d6639ff Use sigaction(SA_NOCLDWAIT) for SIGCHLD handling
signal() semantics are pretty unclearly specified. For example, depending on OS
kernel and libc, the handler may be returned to SIG_DFL (hence the inner call
to read the signal handler). Moving to sigaction() means the behaviour is
consistently defined.

Using SA_NOCLDWAIT also allows us to avoid calling the non-reentrant function
die() in the handler.

Some addditional notes for archival purposes:

* NRK pointed out errno of waitpid could also theoretically get clobbered.
* The original patch was iterated on and modified by NRK and Hiltjo:
  * SIG_DFL was changed to SIG_IGN, this is required, atleast on older systems
    such as tested on Slackware 11.
  * signals are not blocked using sigprocmask, because in theory it would
    briefly for example also ignore a SIGTERM signal. It is OK if waitpid() is (in
    theory interrupted).

POSIX reference:
"Consequences of Process Termination":
https://pubs.opengroup.org/onlinepubs/9699919799/functions/_Exit.html#tag_16_01_03_01
2023-01-28 13:34:43 +01:00
Chris Down
89f9905714 grabkeys: Avoid missing events when a keysym maps to multiple keycodes
It's not uncommon for one keysym to map to multiple keycodes. For
example, the "play" button on my keyboard sends keycode 172, but my
bluetooth headphones send keycode 208, both of which map back to
XF86AudioPlay:

    % xmodmap -pke | grep XF86AudioPlay
    keycode 172 = XF86AudioPlay XF86AudioPause XF86AudioPlay XF86AudioPause
    keycode 208 = XF86AudioPlay NoSymbol XF86AudioPlay
    keycode 215 = XF86AudioPlay NoSymbol XF86AudioPlay

This is a problem because the current code only grabs a single one of
these keycodes, which means that events for any other keycode also
mapping to the bound keysym will not be handled by dwm. In my case, this
means that binding XF86AudioPlay does the right thing and correctly
handles my keyboard's keys, but does nothing on my headphones. I'm not
the only person affected by this, there are other reports[0].

In order to fix this, we look at the mappings between keycodes and
keysyms at grabkeys() time and pick out all matching keycodes rather
than just the first one. The keypress() side of this doesn't need any
changes because the keycode gets converted back to a canonical keysym
before any action is taken.

0: https://github.com/cdown/dwm/issues/11
2022-12-07 23:06:26 +01:00
Hiltjo Posthuma
ba56fe9fea Revert "Remove dmenumon variable"
This reverts commit c2b748e793.

Revert back this change. It seems to not be an edge-case anymore since
multiple users have asked about this new behaviour now.
2022-10-28 16:37:56 +02:00
12 changed files with 239 additions and 170 deletions

20
.gitignore vendored Normal file
View File

@@ -0,0 +1,20 @@
# Ignore dotfiles by default
.*
# Ignore common (generated) file types
*.a
*.o
*.d
*.so
*.out
*.hex
*.gz
# Ignore specific project files
compile_commands.json
/bin/
/config.mk
# Unignore specific files.
!.gitignore
!.clang-format

View File

@@ -1,5 +1,6 @@
MIT/X Consortium License
© 2010-2026 Hiltjo Posthuma <hiltjo@codemadness.org>
© 2006-2019 Anselm R Garbe <anselm@garbe.ca>
© 2006-2009 Jukka Salmi <jukka at salmi dot ch>
© 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
@@ -11,7 +12,6 @@ MIT/X Consortium License
© 2008 Martin Hurton <martin dot hurton at gmail dot com>
© 2008 Neale Pickett <neale dot woozle dot org>
© 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>

111
Makefile
View File

@@ -1,51 +1,92 @@
# dwm - dynamic window manager
# See LICENSE file for copyright and license details.
include config.mk
-include config.mk
SRC = drw.c dwm.c util.c
OBJ = ${SRC:.c=.o}
VERSION = 6.7
all: options dwm
INSTALL ?= install
GZIP ?= gzip
TAR ?= tar
options:
@echo dwm build options:
@echo "CFLAGS = ${CFLAGS}"
@echo "LDFLAGS = ${LDFLAGS}"
@echo "CC = ${CC}"
CFLAGS := -Os $(CFLAGS) -g -std=gnu99\
-Wall -Wextra -Wpedantic -Wno-pointer-arith
CPPFLAGS := -DNDEBUG $(CPPFLAGS)\
-DVERSION=\"$(VERSION)\" -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=700L -DXINERAMA
LDFLAGS := -flto $(LDFLAGS)
LDLIBS := $(LDLIBS) -lm
.c.o:
${CC} -c ${CFLAGS} $<
# Use pkg-config to locate dependencies, and set the correct flags.
ifeq (,$(shell command -v pkg-config))
$(error Failed to locate pkg-config, please make sure it is installed or acessible through PATH.)
else
CPPFLAGS += $(shell pkg-config --cflags-only-I fontconfig freetype2 x11 xft xinerama)
LDFLAGS += $(shell pkg-config --libs-only-L fontconfig freetype2 x11 xft xinerama)
LDLIBS += $(shell pkg-config --libs-only-l fontconfig freetype2 x11 xft xinerama)
endif
${OBJ}: config.h config.mk
SRC := $(wildcard src/*.c)
OBJ := $(addsuffix .o,$(SRC))
DEP := $(addsuffix .d,$(SRC))
config.h:
cp config.def.h $@
# Set Q to @ to silence commands being printed, unless --no-silent has been set
ifeq (0, $(words $(findstring --no-silent,$(MAKEFLAGS))))
Q=@
endif
dwm: ${OBJ}
${CC} -o $@ ${OBJ} ${LDFLAGS}
msg = @printf '%-8s %s\n' "$(1)" "$(2)"
clean:
rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz
.PHONY:
all: bin/dwm
dist: clean
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}
.PHONY:
install: $(DESTDIR)/bin/dwm $(DESTDIR)/share/man/man1/dwm.1.gz
$(DESTDIR)/bin/%: bin/%
$(call msg,INSTALL,$@)
$(Q)$(INSTALL) -D -m0755 $< $@
install: all
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
$(DESTDIR)/share/man/%: man/%
$(call msg,INSTALL,$@)
$(Q)$(INSTALL) -D -m0644 $< $@
.PHONY:
uninstall:
rm -f ${DESTDIR}${PREFIX}/bin/dwm\
${DESTDIR}${MANPREFIX}/man1/dwm.1
$(Q)$(RM) $(DESTDIR)/bin/dwm\
$(DESTDIR)/share/man/man1/dwm.1.gz
.PHONY: all options clean dist install uninstall
.PHONY:
clean:
$(call msg,CLEAN,src/)
$(Q)$(RM) $(OBJ)
$(call msg,CLEAN,man/)
$(Q)$(RM) $(MAN)
$(call msg,CLEAN,bin/)
$(Q)$(RM) -r bin/
# Links together the object files into the final binary.
bin/dwm: $(OBJ) | bin/
$(call msg,LD,$@)
$(Q)$(CC) $(LDFLAGS) $(LDLIBS) -o $@ $^
# Compiles C sources into Object files
%.c.o: %.c
$(call msg,CC,$@)
$(Q)$(CC) -c $(CPPFLAGS) $(CFLAGS) -o $@ $<
# Compress files requested for compression using GZIP
%.gz: %
$(call msg,GZIP,$@)
$(Q)$(GZIP) -k $<
# Create directories
%/:
$(call msg-mkdir,$@)
$(Q)mkdir $@
# Generate and include dependencies,
# ignoring any errors that may occur when doing so.
%.c.d: %.c
$(Q)$(CC) -MM $(CPPFLAGS) -MF $@ $<
ifeq (0, $(words $(findstring $(MAKECMDGOALS), clean)))
-include $(DEP)
endif

View File

@@ -1,39 +0,0 @@
# dwm version
VERSION = 6.4
# Customize below to fit your system
# paths
PREFIX = /usr/local
MANPREFIX = ${PREFIX}/share/man
X11INC = /usr/X11R6/include
X11LIB = /usr/X11R6/lib
# Xinerama, comment if you don't want it
XINERAMALIBS = -lXinerama
XINERAMAFLAGS = -DXINERAMA
# freetype
FREETYPELIBS = -lfontconfig -lXft
FREETYPEINC = /usr/include/freetype2
# OpenBSD (uncomment)
#FREETYPEINC = ${X11INC}/freetype2
#MANPREFIX = ${PREFIX}/man
# includes and libs
INCS = -I${X11INC} -I${FREETYPEINC}
LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS}
# flags
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
#CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS}
CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS}
LDFLAGS = ${LIBS}
# Solaris
#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\"
#LDFLAGS = ${LIBS}
# compiler and linker
CC = cc

View File

@@ -36,6 +36,7 @@ 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 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 int refreshrate = 120; /* refresh rate (per second) for client move/resize */
static const Layout layouts[] = {
/* symbol arrange function */
@@ -56,7 +57,8 @@ static const Layout layouts[] = {
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
/* commands */
static const char *dmenucmd[] = { "dmenu_run", "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
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 *termcmd[] = { "st", NULL };
static const Key keys[] = {

View File

@@ -9,54 +9,40 @@
#include "util.h"
#define UTF_INVALID 0xFFFD
#define UTF_SIZ 4
static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
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 int
utf8decode(const char *s_in, long *u, int *err)
{
for (*i = 0; *i < (UTF_SIZ + 1); ++(*i))
if (((unsigned char)c & utfmask[*i]) == utfbyte[*i])
return (unsigned char)c & ~utfmask[*i];
return 0;
}
static size_t
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;
static const unsigned char lens[] = {
/* 0XXXX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 10XXX */ 0, 0, 0, 0, 0, 0, 0, 0, /* invalid */
/* 110XX */ 2, 2, 2, 2,
/* 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;
if (!clen)
return 0;
udecoded = utf8decodebyte(c[0], &len);
if (!BETWEEN(len, 1, UTF_SIZ))
*err = 1;
if (len == 0)
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);
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;
*u = cp;
return len;
}
@@ -192,8 +178,7 @@ drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
die("error, cannot allocate color '%s'", clrname);
}
/* Wrapper to create color schemes. The caller has to call free(3) on the
* returned color scheme when done using it. */
/* Create color schemes. */
Clr *
drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
{
@@ -201,7 +186,7 @@ drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
Clr *ret;
/* need at least two colors for a scheme */
if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor))))
if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(Clr))))
return NULL;
for (i = 0; i < clrcount; i++)
@@ -209,6 +194,30 @@ drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
return ret;
}
void
drw_clr_free(Drw *drw, Clr *c)
{
if (!drw || !c)
return;
/* c is typedef XftColor Clr */
XftColorFree(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen), c);
}
void
drw_scm_free(Drw *drw, Clr *scm, size_t clrcount)
{
size_t i;
if (!drw || !scm)
return;
for (i = 0; i < clrcount; i++)
drw_clr_free(drw, &scm[i]);
free(scm);
}
void
drw_setfontset(Drw *drw, Fnt *set)
{
@@ -238,11 +247,11 @@ drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int
int
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
{
int i, ty, ellipsis_x = 0;
unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len;
int ty, ellipsis_x = 0;
unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, hash, h0, h1;
XftDraw *d = NULL;
Fnt *usedfont, *curfont, *nextfont;
int utf8strlen, utf8charlen, render = x || y || w || h;
int utf8strlen, utf8charlen, utf8err, render = x || y || w || h;
long utf8codepoint = 0;
const char *utf8str;
FcCharSet *fccharset;
@@ -251,9 +260,8 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
XftResult result;
int charexists = 0, overflow = 0;
/* keep track of a couple codepoints for which we have no match. */
enum { nomatches_len = 64 };
static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches;
static unsigned int ellipsis_width = 0;
static unsigned int nomatches[128], ellipsis_width, invalid_width;
static const char invalid[] = "<EFBFBD>";
if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts)
return 0;
@@ -263,6 +271,8 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
} else {
XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
if (w < lpad)
return x + w;
d = XftDrawCreate(drw->dpy, drw->drawable,
DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen));
@@ -273,12 +283,14 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
usedfont = drw->fonts;
if (!ellipsis_width && render)
ellipsis_width = drw_fontset_getwidth(drw, "...");
if (!invalid_width && render)
invalid_width = drw_fontset_getwidth(drw, invalid);
while (1) {
ew = ellipsis_len = utf8strlen = 0;
ew = ellipsis_len = utf8err = utf8charlen = utf8strlen = 0;
utf8str = text;
nextfont = NULL;
while (*text) {
utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
utf8charlen = utf8decode(text, &utf8codepoint, &utf8err);
for (curfont = drw->fonts; curfont; curfont = curfont->next) {
charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
if (charexists) {
@@ -300,9 +312,9 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
else
utf8strlen = ellipsis_len;
} else if (curfont == usedfont) {
utf8strlen += utf8charlen;
text += utf8charlen;
ew += tmpw;
utf8strlen += utf8err ? 0 : utf8charlen;
ew += utf8err ? 0 : tmpw;
} else {
nextfont = curfont;
}
@@ -310,7 +322,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
}
}
if (overflow || !charexists || nextfont)
if (overflow || !charexists || nextfont || utf8err)
break;
else
charexists = 0;
@@ -325,6 +337,12 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
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);
@@ -338,11 +356,14 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
* character must be drawn. */
charexists = 1;
for (i = 0; i < nomatches_len; ++i) {
/* avoid calling XftFontMatch if we know we won't find a match */
if (utf8codepoint == nomatches.codepoint[i])
goto no_match;
}
hash = (unsigned int)utf8codepoint;
hash = ((hash >> 16) ^ hash) * 0x21F0AAAD;
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();
FcCharSetAddChar(fccharset, utf8codepoint);
@@ -371,7 +392,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
curfont->next = usedfont;
} else {
xfont_free(usedfont);
nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint;
nomatches[nomatches[h0] ? h1 : h0] = utf8codepoint;
no_match:
usedfont = drw->fonts;
}

View File

@@ -40,7 +40,9 @@ void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned in
/* Colorscheme abstraction */
void drw_clr_create(Drw *drw, Clr *dest, const char *clrname);
void drw_clr_free(Drw *drw, Clr *c);
Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount);
void drw_scm_free(Drw *drw, Clr *scm, size_t clrcount);
/* Cursor abstraction */
Cur *drw_cur_create(Drw *drw, int shape);

View File

@@ -50,7 +50,6 @@
#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \
* MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy)))
#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]))
#define LENGTH(X) (sizeof X / sizeof X[0])
#define MOUSEMASK (BUTTONMASK|PointerMotionMask)
#define WIDTH(X) ((X)->w + 2 * (X)->bw)
#define HEIGHT(X) ((X)->h + 2 * (X)->bw)
@@ -205,7 +204,6 @@ 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);
static void tag(const Arg *arg);
static void tagmon(const Arg *arg);
@@ -488,7 +486,7 @@ cleanup(void)
for (i = 0; i < CurLast; i++)
drw_cur_free(drw, cursor[i]);
for (i = 0; i < LENGTH(colors); i++)
free(scheme[i]);
drw_scm_free(drw, scheme[i], 3);
free(scheme);
XDestroyWindow(dpy, wmcheckwin);
drw_free(drw);
@@ -866,13 +864,14 @@ Atom
getatomprop(Client *c, Atom prop)
{
int di;
unsigned long dl;
unsigned long nitems, dl;
unsigned char *p = NULL;
Atom da, atom = None;
if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM,
&da, &di, &dl, &dl, &p) == Success && p) {
atom = *(Atom *)p;
&da, &di, &nitems, &dl, &p) == Success && p) {
if (nitems > 0)
atom = *(Atom *)p;
XFree(p);
}
return atom;
@@ -955,16 +954,26 @@ grabkeys(void)
{
updatenumlockmask();
{
unsigned int i, j;
unsigned int i, j, k;
unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
KeyCode code;
int start, end, skip;
KeySym *syms;
XUngrabKey(dpy, AnyKey, AnyModifier, root);
for (i = 0; i < LENGTH(keys); i++)
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);
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++)
/* skip modifier codes, we do that ourselves */
if (keys[i].keysym == syms[(k - start) * skip])
for (j = 0; j < LENGTH(modifiers); j++)
XGrabKey(dpy, k,
keys[i].mod | modifiers[j],
root, True,
GrabModeAsync, GrabModeAsync);
XFree(syms);
}
}
@@ -1163,7 +1172,7 @@ movemouse(const Arg *arg)
handler[ev.type](&ev);
break;
case MotionNotify:
if ((ev.xmotion.time - lasttime) <= (1000 / 60))
if ((ev.xmotion.time - lasttime) <= (1000 / refreshrate))
continue;
lasttime = ev.xmotion.time;
@@ -1317,7 +1326,7 @@ resizemouse(const Arg *arg)
handler[ev.type](&ev);
break;
case MotionNotify:
if ((ev.xmotion.time - lasttime) <= (1000 / 60))
if ((ev.xmotion.time - lasttime) <= (1000 / refreshrate))
continue;
lasttime = ev.xmotion.time;
@@ -1533,9 +1542,16 @@ setup(void)
int i;
XSetWindowAttributes wa;
Atom utf8string;
struct sigaction sa;
/* clean up any zombies immediately */
sigchld(0);
/* do not transform children into zombies when they terminate */
sigemptyset(&sa.sa_mask);
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 */
screen = DefaultScreen(dpy);
@@ -1628,21 +1644,23 @@ 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
spawn(const Arg *arg)
{
struct sigaction sa;
if (arg->v == dmenucmd)
dmenumon[0] = '0' + selmon->num;
if (fork() == 0) {
if (dpy)
close(ConnectionNumber(dpy));
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);
die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]);
}
@@ -1833,7 +1851,7 @@ updatebarpos(Monitor *m)
}
void
updateclientlist()
updateclientlist(void)
{
Client *c;
Monitor *m;

View File

@@ -5,6 +5,7 @@
#include <X11/Xlib.h>
#include <X11/Xutil.h>
/*
int main(void) {
Display *d;
Window r, f, t = None;
@@ -40,3 +41,4 @@ int main(void) {
XCloseDisplay(d);
exit(0);
}
*/

View File

@@ -1,4 +1,5 @@
/* See LICENSE file for copyright and license details. */
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -10,17 +11,17 @@ 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] == ':') {
fputc(' ', stderr);
perror(NULL);
} else {
fputc('\n', stderr);
}
if (fmt[0] && fmt[strlen(fmt)-1] == ':')
fprintf(stderr, " %s", strerror(saved_errno));
fputc('\n', stderr);
exit(1);
}

View File

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