~martanne/dwm-win32

90f3671e8a28abc86ed56cbae8c08ff522dfa776 — Marc Andre Tanner 15 years ago alpha1
Import dwm-win32-alpha1
6 files changed, 1381 insertions(+), 0 deletions(-)

A LICENSE.txt
A Makefile
A README.txt
A config.def.h
A config.mk
A dwm-win32.c
A  => LICENSE.txt +31 -0
@@ 1,31 @@
MIT/X Consortium License

© 2006-2009 Anselm R Garbe <garbeam at gmail dot com>
© 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
© 2006-2007 Jukka Salmi <jukka at salmi dot ch>
© 2007 Premysl Hruby <dfenze at gmail dot com>
© 2007 Szabolcs Nagy <nszabolcs at gmail dot com>
© 2007 Christof Musik <christof at sendfax dot de>
© 2007-2008 Enno Gottox Boland <gottox at s01 dot de>
© 2007-2008 Peter Hartlich <sgkkr at hartlich dot com>
© 2008 Martin Hurton <martin dot hurton at gmail dot com>
© 2008 Neale Pickett <neale at woozle dot org>
© 2009 Marc Andre Tanner <mat at brain-dump dot org>

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

A  => Makefile +60 -0
@@ 1,60 @@
# dwm-win32 - dynamic window manager for win32
# See LICENSE file for copyright and license details.

include config.mk

SRC = dwm-win32.c
OBJ = ${SRC:.c=.o}

all: options dwm-win32

options:
	@echo dwm-win32 build options:
	@echo "CFLAGS   = ${CFLAGS}"
	@echo "LDFLAGS  = ${LDFLAGS}"
	@echo "CC       = ${CC}"

.c.o:
	@echo CC $<
	@${CC} -c ${CFLAGS} $<

${OBJ}: config.h config.mk

config.h:
	@echo creating $@ from config.def.h
	@cp config.def.h $@

dwm-win32: ${OBJ}
	@echo CC -o $@
	@${CC} -o $@ ${OBJ} ${LDFLAGS}

clean:
	@echo cleaning
	@rm -f dwm-win32.exe ${OBJ} dwm-win32-${VERSION}.tar.gz

dist: clean
	@echo creating dist tarball
	@mkdir -p dwm-win32-${VERSION}
	@cp -R LICENSE.txt Makefile README.txt config.def.h config.mk \
		${SRC} dwm-win32-${VERSION}
	@tar -cf dwm-win32-${VERSION}.tar dwm-win32-${VERSION}
	@gzip dwm-win32-${VERSION}.tar
	@rm -rf dwm-win32-${VERSION}

install: all
	@echo installing executable file to ${DESTDIR}${PREFIX}/bin
	@mkdir -p ${DESTDIR}${PREFIX}/bin
	@cp -f dwm-win32 ${DESTDIR}${PREFIX}/bin
	@chmod 755 ${DESTDIR}${PREFIX}/bin/dwm-win32
	@echo installing manual page to ${DESTDIR}${MANPREFIX}/man1
	@mkdir -p ${DESTDIR}${MANPREFIX}/man1
	@sed "s/VERSION/${VERSION}/g" < dwm-win32.1 > ${DESTDIR}${MANPREFIX}/man1/dwm-win32.1
	@chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm-win32.1

uninstall:
	@echo removing executable file from ${DESTDIR}${PREFIX}/bin
	@rm -f ${DESTDIR}${PREFIX}/bin/dwm-win32
	@echo removing manual page from ${DESTDIR}${MANPREFIX}/man1
	@rm -f ${DESTDIR}${MANPREFIX}/man1/dwm-win32.1

.PHONY: all options clean dist install uninstall

A  => README.txt +17 -0
@@ 1,17 @@
dwm-win32 is a port of the well known X11 window manager dwm to the Microsoft Windows platform.

TODO
 - focus handling / stealling / floating windows (ex. WinSCP)
 - fix redraw problems which happen from time to time
 - fullscreen windows? Screensaver?
 - window border currently it's drawn into the client area which is bad
 - status text via stdin or a separate tool
 - crash handler which makes all windows visible restores borders etc
 - code cleanups all over the place
 - multi head support?

 [ - introduce a ShellProc function and register it with SetWindowsHookEx
     to handle window events instead of current mechanism in WndProc which
     is based on the shellhookid but seems to be bugy in some cases?.
     Unfortunately the SetWindowsHookEx thingy seems to require a separate 
     dll which sucks. ]

A  => config.def.h +99 -0
@@ 1,99 @@
/* See LICENSE file for copyright and license details. */

/* appearance */
static const COLORREF normbordercolor = RGB(204, 204, 204);
static const COLORREF normbgcolor     = RGB(204, 204, 204);
static const COLORREF normfgcolor     = RGB(0,0,0);
static const COLORREF selbordercolor  = RGB(0,102,255);
static const COLORREF selbgcolor      = RGB(0,102,255);
static const COLORREF selfgcolor      = RGB(255,255,255);

static const unsigned int borderpx    = 2;        /* border pixel of windows */
static const unsigned int textmargin  = 5;        /* margin for the text displayed on the bar */
static bool showbar                   = true;     /* false means no bar */
static bool topbar                    = true;     /* false means bottom bar */

/* tagging */
static const char tags[][MAXTAGLEN] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
static unsigned int tagset[] = {1, 1}; /* after start, first tag is selected */

static Rule rules[] = {
	/* class                   title                      tags mask     isfloating */
	{ "MozillaUIWindowClass",  "- Mozilla Firefox",       1 << 8,       false },
};

/* layout(s) */
static float mfact      = 0.55; /* factor of master area size [0.05..0.95] */

static Layout layouts[] = {
	/* symbol     arrange function */
	{ "[]=",      tile },    /* first entry is default */
	{ "><>",      NULL },    /* no layout function means floating behavior */
	{ "[M]",      monocle },
};

/* key definitions */
#define MODKEY 		(MOD_CONTROL | MOD_ALT)
#define TAGKEYS(KEY,TAG) \
	{ MODKEY,                       KEY,      view,           {.ui = 1 << TAG} }, \
	{ MODKEY|MOD_CONTROL,           KEY,      toggleview,     {.ui = 1 << TAG} }, \
	{ MODKEY|MOD_SHIFT,             KEY,      tag,            {.ui = 1 << TAG} }, \
	{ MODKEY|MOD_CONTROL|MOD_SHIFT, KEY,      toggletag,      {.ui = 1 << TAG} },

/* helper for spawning shell commands in the pre dwm-5.0 fashion */
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }

/* commands */
static const char *termcmd[]  = { "cmd.exe", NULL };

static Key keys[] = {
	/* modifier                     key        function        argument */
	{ MODKEY,                       'P',       spawn,          {.v = termcmd } },
	{ MODKEY|MOD_SHIFT,             VK_RETURN, spawn,          {.v = termcmd } },
	{ MODKEY,                       'B',       togglebar,      {0} },
	{ MODKEY,                       'J',       focusstack,     {.i = +1 } },
	{ MODKEY,                       'K',       focusstack,     {.i = -1 } },
	{ MODKEY,                       'H',       setmfact,       {.f = -0.05} },
	{ MODKEY,                       'L',       setmfact,       {.f = +0.05} },
	{ MODKEY,                       'I',       showclientclassname,  {0} },
	{ MODKEY,                       VK_RETURN, zoom,           {0} },
	{ MODKEY,                       VK_TAB,    view,           {0} },
	{ MODKEY|MOD_SHIFT,             'C',       killclient,     {0} },
	{ MODKEY,                       'T',       setlayout,      {.v = &layouts[0]} },
	{ MODKEY,                       'F',       setlayout,      {.v = &layouts[1]} },
	{ MODKEY,                       'M',       setlayout,      {.v = &layouts[2]} },
	{ MODKEY,                       VK_SPACE,  setlayout,      {0} },
	{ MODKEY|MOD_SHIFT,             VK_SPACE,  togglefloating, {0} },
	{ MODKEY,                       'N',       toggleborder,   {0} },
	{ MODKEY,                       'E',       toggleexplorer, {0} },

	{ MODKEY,                       '0',       view,           {.ui = ~0 } },
	{ MODKEY|MOD_SHIFT,             '0',       tag,            {.ui = ~0 } },
	TAGKEYS(                        '1',                       0)
	TAGKEYS(                        '2',                       1)
	TAGKEYS(                        '3',                       2)
	TAGKEYS(                        '4',                       3)
	TAGKEYS(                        '5',                       4)
	TAGKEYS(                        '6',                       5)
	TAGKEYS(                        '7',                       6)
	TAGKEYS(                        '8',                       7)
	TAGKEYS(                        '9',                       8)
	{ MODKEY,                       'Q',       quit,           {0} },
};

/* button definitions */
/* click can be a tag number (starting at 0), ClkLtSymbol, ClkStatusText or ClkWinTitle */
static Button buttons[] = {
	/* click                button event type     modifier keys    function        argument */
	{ ClkLtSymbol,          WM_LBUTTONDOWN,       0,               setlayout,      {0} },
	{ ClkLtSymbol,          WM_RBUTTONDOWN,       0,               setlayout,      {.v = &layouts[2]} },
	{ ClkWinTitle,          WM_MBUTTONDOWN,       0,               zoom,           {0} },
	{ ClkStatusText,        WM_MBUTTONDOWN,       0,               spawn,          {.v = termcmd } },
#if 0
	{ ClkClientWin,         WM_MBUTTONDOWN,       MODKEY,          togglefloating, {0} },
#endif
	{ ClkTagBar,            WM_LBUTTONDOWN,       0,               view,           {0} },
	{ ClkTagBar,            WM_RBUTTONDOWN,       0,               toggleview,     {0} },
	{ ClkTagBar,            WM_LBUTTONDOWN,       MODKEY,          tag,            {0} },
	{ ClkTagBar,            WM_RBUTTONDOWN,       MODKEY,          toggletag,      {0} },
};

A  => config.mk +16 -0
@@ 1,16 @@
# dwm-win32 version
VERSION = alpha1

# Customize below to fit your system

# paths
PREFIX = /usr/local
MANPREFIX = ${PREFIX}/share/man

# flags
CPPFLAGS = -DVERSION=\"${VERSION}\"
CFLAGS = -std=c99 -pedantic -Wall -Os ${CPPFLAGS}
LDFLAGS = -s -mwindows

# compiler and linker
CC = gcc

A  => dwm-win32.c +1158 -0
@@ 1,1158 @@
/* See LICENSE file for copyright and license details.
 *
 * This is a port of the popular X11 window manager dwm to Microsoft Windows.
 * It was originally started by Marc Andre Tanner <mat at brain-dump dot org>
 *
 * Each child of the root window is called a client. Clients are organized 
 * in a global linked client list, the focus history is remembered through 
 * a global stack list. Each client contains a bit array to indicate the 
 * tags of a client.
 *
 * Keys and tagging rules are organized as arrays and defined in config.h.
 *
 * To understand everything else, start reading main().
 */

#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT		0x0500

#include <windows.h>
#include <winuser.h>
#include <stdlib.h>
#include <stdio.h>
#include <shellapi.h>
#include <stdbool.h>

#define NAME			"dwm-win32" 	/* Used for window name/class */

/* macros */
#define ISVISIBLE(x)            (x->tags & tagset[seltags])
#define LENGTH(x)               (sizeof x / sizeof x[0])
#define MAX(a, b)               ((a) > (b) ? (a) : (b))
#define MIN(a, b)               ((a) < (b) ? (a) : (b))
#define MAXTAGLEN               16
#define WIDTH(x)                ((x)->w + 2 * (x)->bw)
#define HEIGHT(x)               ((x)->h + 2 * (x)->bw)
#define TAGMASK                 ((int)((1LL << LENGTH(tags)) - 1))
#define TEXTW(x)                (textnw(x, strlen(x)))
#ifdef NDEBUG
# define debug(format, args...)
#else
# define debug eprint
#endif

/* enums */
enum { CurNormal, CurResize, CurMove, CurLast };		/* cursor */
enum { ColBorder, ColFG, ColBG, ColLast };			/* color */
enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle };	/* clicks */

typedef struct {
	int x, y, w, h;
	unsigned long norm[ColLast];
	unsigned long sel[ColLast];
	HDC hdc;
} DC; /* draw context */

DC dc;

typedef union {
	int i;
	unsigned int ui;
	float f;
	void *v;
} Arg;

typedef struct {
	unsigned int click;
	unsigned int button;
	unsigned int key;
	void (*func)(const Arg *arg);
	const Arg arg;
} Button;

typedef struct Client Client;
struct Client {
	HWND hwnd;
	int x, y, w, h;
	int bw; // XXX: useless?
	unsigned int tags;
	bool isminimized, isfixed, isfloating, isurgent, ignore, border;
	Client *next;
	Client *snext;
};

typedef struct {
	unsigned int mod;
	unsigned int key;
	void (*func)(const Arg *);
	const Arg arg;
} Key;

typedef struct {
	const char *symbol;
	void (*arrange)(void);
} Layout;

typedef struct {
	const char *class;
	const char *title;
	unsigned int tags;
	bool isfloating;
} Rule;

/* function declarations */
static void applyrules(Client *c);
static void arrange(void);
static void attach(Client *c);
static void attachstack(Client *c);
static void cleanup();
static void clearurgent(Client *c);
static void detach(Client *c);
static void detachstack(Client *c);
static void die(const char *errstr, ...);
static void drawbar(void);
static void drawsquare(bool filled, bool empty, bool invert, unsigned long col[ColLast]);
static void drawtext(const char *text, unsigned long col[ColLast], bool invert);
void drawborder(Client *c, COLORREF color);
void eprint(const char *errstr, ...);
static void focus(Client *c);
static void focusstack(const Arg *arg);
static Client *getclient(HWND hwnd);
LPSTR getclientclassname(HWND hwnd);
LPSTR getclienttitle(HWND hwnd);
static void grabkeys(HWND hwnd);
static void killclient(const Arg *arg);
static void monocle(void);
static Client *nexttiled(Client *c);
static void quit(const Arg *arg);
static void resize(Client *c, int x, int y, int w, int h);
static void restack(void);
static BOOL CALLBACK scan(HWND hwnd, LPARAM lParam);
static void setborder(Client *c, bool border);
static void setvisibility(HWND hwnd, bool visibility);
static void setlayout(const Arg *arg);
static void setmfact(const Arg *arg);
static void setup(HINSTANCE hInstance);
static void setupbar(HINSTANCE hInstance);
static void showclientclassname(const Arg *arg); 
static void showhide(Client *c);
static void spawn(const Arg *arg);
static void tag(const Arg *arg);
static int textnw(const char *text, unsigned int len);
static void tile(void);
static void togglebar(const Arg *arg);
static void toggleborder(const Arg *arg);
static void toggleexplorer(const Arg *arg);
static void togglefloating(const Arg *arg);
static void toggletag(const Arg *arg);
static void toggleview(const Arg *arg);
static void unmanage(Client *c);
static void updatebar(void);
static void updategeom(void);
static void updatestatus(void);
static void view(const Arg *arg);
static void zoom(const Arg *arg);

/* Shell Hook stuff */
typedef BOOL (*RegisterShellHookWindowProc) (HWND);
RegisterShellHookWindowProc RegisterShellHookWindow;

/* XXX: should be in a system header, no? */
typedef struct {
    HWND    hwnd;
    RECT    rc;
} SHELLHOOKINFO, *LPSHELLHOOKINFO;

/* variables */
static HWND dwmhwnd, barhwnd;
static char stext[256];
static int sx, sy, sw, sh; /* X display screen geometry x, y, width, height */ 
static int by, bh, blw;    /* bar geometry y, height and layout symbol width */
static int wx, wy, ww, wh; /* window area geometry x, y, width, height, bar excluded */
static unsigned int seltags = 0, sellt = 0;

static Client *clients = NULL;
static Client *sel = NULL;
static Client *stack = NULL;
static Layout *lt[] = { NULL, NULL };
static UINT shellhookid;	/* Window Message id */

/* configuration, allows nested code to access above variables */
#include "config.h"

/* compile-time check if all tags fit into an unsigned int bit array. */
struct NumTags { char limitexceeded[sizeof(unsigned int) * 8 < LENGTH(tags) ? -1 : 1]; };

/* function implementations */
void
applyrules(Client *c) {
	unsigned int i;
	Rule *r;

	/* rule matching */
	for(i = 0; i < LENGTH(rules); i++) {
		r = &rules[i];
		if((!r->title || strstr(getclienttitle(c->hwnd), r->title))
		&& (!r->class || strstr(getclientclassname(c->hwnd), r->class))) {
			c->isfloating = r->isfloating;
			c->tags |= r->tags & TAGMASK ? r->tags & TAGMASK : tagset[seltags]; 
		}
	}
	if(!c->tags)
		c->tags = tagset[seltags];
}

void
arrange(void) {
	showhide(stack);
	focus(NULL);
	if(lt[sellt]->arrange)
		lt[sellt]->arrange();
	restack();
}

void
attach(Client *c) {
	c->next = clients;
	clients = c;
}

void
attachstack(Client *c) {
	c->snext = stack;
	stack = c;
}

void
buttonpress(unsigned int button, POINTS *point) {
	unsigned int i, x, click;
	Arg arg = {0};

	/* XXX: hack */
	dc.hdc = GetWindowDC(barhwnd);

	i = x = 0;
	debug(" buttonpress: x:%d y:%d\n", point->x, point->y);
	do { x += TEXTW(tags[i]);
		debug("  %d: %d > %d\n", i, point->x, x);
	} while(point->x >= x && ++i < LENGTH(tags));
	if(i < LENGTH(tags)) {
		click = ClkTagBar;
		arg.ui = 1 << i;
	}
	else if(point->x < x + blw)
		click = ClkLtSymbol;
	else if(point->x > wx + ww - TEXTW(stext))
		click = ClkStatusText;
	else
		click = ClkWinTitle;

	if (GetKeyState(VK_SHIFT) < 0)
		return;

	for(i = 0; i < LENGTH(buttons); i++)
		// XXX: is GetKeyState really working?
		if(click == buttons[i].click && buttons[i].func && buttons[i].button == button
		   && (!buttons[i].key || GetKeyState(buttons[i].key) < 0))
			buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
}

void
cleanup() {
	int i;
	Arg a = {.ui = ~0};
	Layout foo = { "", NULL };
	
	for(i = 0; i < LENGTH(keys); i++) {
		UnregisterHotKey(dwmhwnd, i);
	}

	DeregisterShellHookWindow(dwmhwnd);

	view(&a);
	lt[sellt] = &foo;
	while(stack)
		unmanage(stack);

	DestroyWindow(dwmhwnd);
}

void
clearurgent(Client *c) {

}

void
detach(Client *c) {
	Client **tc;

	for(tc = &clients; *tc && *tc != c; tc = &(*tc)->next);
	*tc = c->next;
}

void
detachstack(Client *c) {
	Client **tc;

	for(tc = &stack; *tc && *tc != c; tc = &(*tc)->snext);
	*tc = c->snext;
}

void
die(const char *errstr, ...) {
	va_list ap;

	va_start(ap, errstr);
	vfprintf(stderr, errstr, ap);
	va_end(ap);
	cleanup();
	exit(EXIT_FAILURE);
}

void
drawbar(void) {
	dc.hdc = GetWindowDC(barhwnd);

	dc.h = bh;

	int x;
	unsigned int i, occ = 0, urg = 0;
	unsigned long *col;
	Client *c;

	for(c = clients; c; c = c->next) {
		occ |= c->tags;
		if(c->isurgent)
			urg |= c->tags;
	}

	dc.x = 0;
	for(i = 0; i < LENGTH(tags); i++) {
		dc.w = TEXTW(tags[i]);
		col = tagset[seltags] & 1 << i ? dc.sel : dc.norm;
		drawtext(tags[i], col, urg & 1 << i);
		drawsquare(sel && sel->tags & 1 << i, occ & 1 << i, urg & 1 << i, col);
		dc.x += dc.w;
	}
	if(blw > 0) {
		dc.w = blw;
		drawtext(lt[sellt]->symbol, dc.norm, false);
		x = dc.x + dc.w;
	}
	else
		x = dc.x;
	dc.w = TEXTW(stext);
	dc.x = ww - dc.w;
	if(dc.x < x) {
		dc.x = x;
		dc.w = ww - x;
	}
	drawtext(stext, dc.norm, false);

	if((dc.w = dc.x - x) > bh) {
		dc.x = x;
		if(sel) {
			drawtext(getclienttitle(sel->hwnd), dc.sel, false);
			drawsquare(sel->isfixed, sel->isfloating, false, dc.sel);
		}
		else
			drawtext(NULL, dc.norm, false);
	}

	ReleaseDC(barhwnd, dc.hdc);
}

void
drawsquare(bool filled, bool empty, bool invert, COLORREF col[ColLast]) {
	static int size = 5;
	RECT r = { .left = dc.x + 1, .top = dc.y + 1, .right = dc.x + size, .bottom = dc.y + size };

	HBRUSH brush = CreateSolidBrush(col[invert ? ColBG : ColFG]);
	SelectObject(dc.hdc, brush);

	if(filled) {
		FillRect(dc.hdc, &r, brush);
	} else if(empty) {
		FillRect(dc.hdc, &r, brush);
	}
	DeleteObject(brush);
}

void
drawtext(const char *text, COLORREF col[ColLast], bool invert) {
	RECT r = { .left = dc.x, .top = dc.y, .right = dc.x + dc.w, .bottom = dc.y + dc.h };

	HPEN pen = CreatePen(PS_SOLID, borderpx, selbordercolor);
	HBRUSH brush = CreateSolidBrush(col[invert ? ColFG : ColBG]);
	SelectObject(dc.hdc, pen);
	SelectObject(dc.hdc, brush);
	FillRect(dc.hdc, &r, brush);

	DeleteObject(brush);
	DeleteObject(pen);

	SetBkMode(dc.hdc, TRANSPARENT);
	SetTextColor(dc.hdc, col[invert ? ColBG : ColFG]);

	HFONT font = (HFONT)GetStockObject(SYSTEM_FONT); 
	SelectObject(dc.hdc, font);

	DrawText(dc.hdc, text, -1, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}

void
eprint(const char *errstr, ...) {
	va_list ap;

	va_start(ap, errstr);
	vfprintf(stderr, errstr, ap);
	fflush(stderr);
	va_end(ap);
}

void
focus(Client *c) {
	if(!c || !ISVISIBLE(c))
		for(c = stack; c && !ISVISIBLE(c); c = c->snext);
	if(sel && sel != c)
		drawborder(sel, normbordercolor);
	if(c) {
		if(c->isurgent)
			clearurgent(c);
		detachstack(c);
		attachstack(c);
		drawborder(c, selbordercolor);
		SetForegroundWindow(c->hwnd);
	}
	sel = c;
	drawbar();
}

void
focusstack(const Arg *arg) {
	Client *c = NULL, *i;

	if(!sel)
		return;
	if (arg->i > 0) {
		for(c = sel->next; c && !ISVISIBLE(c); c = c->next);
		if(!c)
			for(c = clients; c && !ISVISIBLE(c); c = c->next);
	}
	else {
		for(i = clients; i != sel; i = i->next)
			if(ISVISIBLE(i))
				c = i;
		if(!c)
			for(; i; i = i->next)
				if(ISVISIBLE(i))
					c = i;
	}
	if(c) {
		focus(c);
		restack();
	}
}

Client *
getclient(HWND hwnd) {
	Client *c;

	for(c = clients; c && c->hwnd != hwnd; c = c->next);
	return c;
}

LPSTR
getclientclassname(HWND hwnd) {
	static TCHAR buf[128];
	GetClassName(hwnd, buf, sizeof buf);
	return buf;
}

LPSTR
getclienttitle(HWND hwnd) {
	static TCHAR buf[128];
	GetWindowText(hwnd, buf, sizeof buf);
	return buf;
}

void
grabkeys(HWND hwnd) {
	int i;
	for (i = 0; i < LENGTH(keys); i++) {
		RegisterHotKey(hwnd, i, keys[i].mod, keys[i].key);
	}
}

bool
ismanageable(HWND hwnd){
	debug("ismanageable: %d %s\n", hwnd, getclienttitle(hwnd));
	debug(" visible: %d\n", IsWindowVisible(hwnd));
	debug(" parent : %d\n", GetParent(hwnd));

	/* Some criteria for windows to be tiled */
	if (IsWindowVisible(hwnd) && GetParent(hwnd) == 0) {
		int exstyle = GetWindowLong(hwnd, GWL_EXSTYLE);
		HWND owner = GetWindow(hwnd, GW_OWNER);
		debug(" owner:      %d\n", owner);
		debug(" toolwindow: %d\n", exstyle & WS_EX_TOOLWINDOW);
		debug(" appwindow:  %d\n", exstyle & WS_EX_APPWINDOW);
		if (((exstyle & WS_EX_TOOLWINDOW) == 0 && owner == 0) || ((exstyle & WS_EX_APPWINDOW) && owner != 0)) {
			return true;
		}
	}
	return false;
}

void
killclient(const Arg *arg) {
	if(!sel)
		return;
	PostMessage(sel->hwnd, WM_CLOSE, 0, 0);
}

Client*
manage(HWND hwnd) {
	debug(" manage %s\n", getclienttitle(hwnd));
	static Client cz;
	Client *c;

	WINDOWINFO wi = {
		.cbSize = sizeof(WINDOWINFO),
	};


	if (!GetWindowInfo(hwnd, &wi))
		return NULL;

	if(!(c = malloc(sizeof(Client))))
		die("fatal: could not malloc() %u bytes\n", sizeof(Client));

	*c = cz;
	c->hwnd = hwnd;

	static WINDOWPLACEMENT wp = {
		.length = sizeof(WINDOWPLACEMENT),
		.showCmd = SW_RESTORE,
	};

	SetWindowPlacement(hwnd, &wp);
	
	/* maybe we could also filter based on 
	 * WS_MINIMIZEBOX and WS_MAXIMIZEBOX
	 */
	c->isfloating = wi.dwStyle & WS_POPUP;

	debug(" window style: %d\n", wi.dwStyle);
	debug("     minimize: %d\n", wi.dwStyle & WS_MINIMIZEBOX);
	debug("     maximize: %d\n", wi.dwStyle & WS_MAXIMIZEBOX);
	debug("        popup: %d\n", wi.dwStyle & WS_POPUP);
	debug("   isfloating: %d\n", c->isfloating);

	applyrules(c);

	setborder(c, c->isfloating);

	if (c->isfloating) {
		debug(" new floating window: x: %d y: %d w: %d h: %d\n", wi.rcWindow.left, wi.rcWindow.top, wi.rcWindow.right - wi.rcWindow.left, wi.rcWindow.bottom - wi.rcWindow.top);
		resize(c, wi.rcWindow.left, wi.rcWindow.top, wi.rcWindow.right - wi.rcWindow.left, wi.rcWindow.bottom - wi.rcWindow.top);
	}

	attach(c);
	attachstack(c);
	return c;
}

void
monocle(void) {
	Client *c;

	for(c = nexttiled(clients); c; c = nexttiled(c->next)) {
		resize(c, wx, wy, ww - 2 * c->bw, wh - 2 * c->bw);
	}
}

Client *
nexttiled(Client *c) {
	for(; c && (c->isfloating || c->isminimized || !ISVISIBLE(c)); c = c->next);
	return c;
}

void
quit(const Arg *arg) {
	PostMessage(dwmhwnd, WM_CLOSE, 0, 0);
}

void
resize(Client *c, int x, int y, int w, int h) {
#if 0
	if(w <= 0 || h <= 0)
		return;
#endif
	if(x > sx + sw)
		x = sw - WIDTH(c);
	if(y > sy + sh)
		y = sh - HEIGHT(c);
	if(x + w + 2 * c->bw < sx)
		x = sx;
	if(y + h + 2 * c->bw < sy)
		y = sy;
	if(h < bh)
		h = bh;
	if(w < bh)
		w = bh;
	if(c->x != x || c->y != y || c->w != w || c->h != h) {
		c->x = x;
		c->y = y;
		c->w = w;
		c->h = h;
		debug(" resize %s: x: %d y: %d w: %d h: %d\n", getclienttitle(c->hwnd), x, y, w, h);
		SetWindowPos(c->hwnd, HWND_TOP, c->x, c->y, c->w, c->h, SWP_NOACTIVATE);
	}
}

void
restack(void) {
#if 0
	Client *c;
	XEvent ev;
	XWindowChanges wc;

	drawbar();
	if(!sel)
		return;
	if(sel->isfloating || !lt[sellt]->arrange)
		XRaiseWindow(dpy, sel->win);
	if(lt[sellt]->arrange) {
		wc.stack_mode = Below;
		wc.sibling = barwin;
		for(c = stack; c; c = c->snext)
			if(!c->isfloating && ISVISIBLE(c)) {
				XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc);
				wc.sibling = c->win;
			}
	}
	XSync(dpy, false);
	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
#endif
}

LRESULT CALLBACK barhandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg) {
		case WM_CREATE:
			updatebar();
			break;
		case WM_PAINT: {
			PAINTSTRUCT ps;
			BeginPaint(hwnd, &ps);
			drawbar();
			EndPaint(hwnd, &ps);
			break;
		}
		case WM_LBUTTONDOWN:
		case WM_RBUTTONDOWN:
		case WM_MBUTTONDOWN:
			buttonpress(msg, &MAKEPOINTS(lParam));
			break;
		default:
			return DefWindowProc(hwnd, msg, wParam, lParam); 
	}

	return 0;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg) {
		case WM_CREATE:
			break;
		case WM_CLOSE:
			cleanup();
			break;
		case WM_DESTROY:
			PostQuitMessage(0);
			break;
		case WM_HOTKEY:
			if (wParam > 0 && wParam < LENGTH(keys)) {
				keys[wParam].func(&(keys[wParam ].arg));
			}
			break;
		default:
			if (msg == shellhookid) { /* Handle the shell hook message */
				debug(" SHELLHOOK: %d == %d\n", wParam, HSHELL_WINDOWACTIVATED);
				Client *c = getclient((HWND)lParam);
				switch (wParam) {
					/* The first two events are also trigger if windows
					 * are being hidden or shown because of a tag
					 * switch, therefore we ignore them in this case.
					 */
					case HSHELL_WINDOWCREATED:
						debug("window created: %s\n", getclienttitle((HWND)lParam));
						if (!c && ismanageable((HWND)lParam)) {
							manage((HWND)lParam);
							arrange();
						}
						break;
					case HSHELL_WINDOWDESTROYED:
						if (c)
							if (!c->ignore)
								unmanage(c);
							else
								c->ignore = false;
						else 
							debug(" unmanaged window destroyed");
						break;
					case HSHELL_WINDOWACTIVATED:
						debug(" window activated: %s || %d\n", c ? getclienttitle(c->hwnd) : "unknown", (HWND)lParam);
						if (c)
							focus(c);
						else  {
							/* Some window don't seem to generate 
							 * HSHELL_WINDOWCREATED messages therefore 
						 	 * we check here whether we should manage
						 	 * the window or not.
						 	 */
							if (ismanageable((HWND)lParam)) {
								c = manage((HWND)lParam);
								focus(c);
								arrange();
							} else
								SetForegroundWindow((HWND)lParam);
						}
						break;
					case HSHELL_GETMINRECT:
						debug("min/max event\n");
						c = getclient(((SHELLHOOKINFO*)lParam)->hwnd);
						if (c) {
							WINDOWPLACEMENT winplace;
							winplace.length = sizeof(WINDOWPLACEMENT);
							if (GetWindowPlacement(c->hwnd, &winplace)) 
								c->isminimized = (winplace.showCmd == SW_SHOWMINIMIZED);
							debug(" window state changed: %s\n", c->isminimized ? "minimized" : "maximized");
							arrange();						
						}
						break;
				}
			} else
				return DefWindowProc(hwnd, msg, wParam, lParam); 
	}

	return 0;
}

BOOL CALLBACK 
scan(HWND hwnd, LPARAM lParam) {
	if (ismanageable(hwnd))
		manage(hwnd);
	return TRUE;
}

void
drawborder(Client *c, COLORREF color) {
	HDC hdc = GetWindowDC(c->hwnd);

#if 0
	/* this would be another way, but it uses standard sytem colors */
	RECT area = { .left = 0, .top = 0, .right = c->w, .bottom = c->h };
	DrawEdge(hdc, &area, BDR_RAISEDOUTER | BDR_SUNKENINNER, BF_RECT);
#else
	HPEN pen = CreatePen(PS_SOLID, borderpx, color);
	SelectObject(hdc, pen);
	MoveToEx(hdc, 0, 0, NULL);
	LineTo(hdc, c->w, 0);
	LineTo(hdc, c->w, c->h);
	LineTo(hdc, 0, c->h);
	LineTo(hdc, 0, 0);
	DeleteObject(pen);
#endif
	ReleaseDC(c->hwnd, hdc);
}

void
setborder(Client *c, bool border) {
	if (border) {
		SetWindowLong(c->hwnd, GWL_STYLE, (GetWindowLong(c->hwnd, GWL_STYLE) | (WS_CAPTION | WS_SIZEBOX)));
		SetWindowPos(c->hwnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER); /* XXX: needed */
	} else {
		SetWindowLong(c->hwnd, GWL_STYLE, (GetWindowLong(c->hwnd, GWL_STYLE) & ~(WS_CAPTION | WS_SIZEBOX)));
	}
	c->border = border;
}


void
setvisibility(HWND hwnd, bool visibility) {
	SetWindowPos(hwnd, 0, 0, 0, 0, 0, (visibility ? SWP_SHOWWINDOW : SWP_HIDEWINDOW) | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
}

void
setlayout(const Arg *arg) {
	if(!arg || !arg->v || arg->v != lt[sellt])
		sellt ^= 1;
	if(arg && arg->v)
		lt[sellt] = (Layout *)arg->v;
	if(sel)
		arrange();
	else
		drawbar();
}

/* arg > 1.0 will set mfact absolutly */
void
setmfact(const Arg *arg) {
	float f;

	if(!arg || !lt[sellt]->arrange)
		return;
	f = arg->f < 1.0 ? arg->f + mfact : arg->f - 1.0;
	if(f < 0.1 || f > 0.9)
		return;
	mfact = f;
	arrange();
}

void
setup(HINSTANCE hInstance) {

	lt[0] = &layouts[0];
	lt[1] = &layouts[1 % LENGTH(layouts)];

	/* init appearance */

	dc.norm[ColBorder] = normbordercolor;
	dc.norm[ColBG] = normbgcolor;
	dc.norm[ColFG] = normfgcolor;
	dc.sel[ColBorder] = selbordercolor;
	dc.sel[ColBG] = selbgcolor;
	dc.sel[ColFG] = selfgcolor;

	updategeom();

	WNDCLASSEX winClass;

	winClass.cbSize = sizeof(WNDCLASSEX);
	winClass.style = 0;
	winClass.lpfnWndProc = WndProc;
	winClass.cbClsExtra = 0;
	winClass.cbWndExtra = 0;
	winClass.hInstance = hInstance;
	winClass.hIcon = NULL;
	winClass.hIconSm = NULL;
	winClass.hCursor = NULL;
	winClass.hbrBackground = NULL;
	winClass.lpszMenuName = NULL;
	winClass.lpszClassName = NAME;

	if (!RegisterClassEx(&winClass))
		die("Error registering window class");

	dwmhwnd = CreateWindowEx(0, NAME, NAME, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, hInstance, NULL);

	if (!dwmhwnd)
		die("Error Creating Window");

	grabkeys(dwmhwnd);

	EnumWindows(scan, 0);

	setupbar(hInstance);

	arrange();
	
	/* Get function pointer for RegisterShellHookWindow */
	RegisterShellHookWindow = (RegisterShellHookWindowProc)GetProcAddress(GetModuleHandle("USER32.DLL"), "RegisterShellHookWindow");
	if (!RegisterShellHookWindow)
		die("Could not find RegisterShellHookWindow");
	RegisterShellHookWindow(dwmhwnd);
	/* Grab a dynamic id for the SHELLHOOK message to be used later */
	shellhookid = RegisterWindowMessage("SHELLHOOK");
}

void
setupbar(HINSTANCE hInstance) {

	unsigned int i, w = 0;

	WNDCLASS winClass;
	memset(&winClass, 0, sizeof winClass);

	winClass.style = 0;
	winClass.lpfnWndProc = barhandler;
	winClass.cbClsExtra = 0;
	winClass.cbWndExtra = 0;
	winClass.hInstance = hInstance;
	winClass.hIcon = NULL;
	winClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	winClass.hbrBackground = NULL;
	winClass.lpszMenuName = NULL;
	winClass.lpszClassName = "dwm-bar";

	if (!RegisterClass(&winClass))
		die("Error registering window class");

	barhwnd = CreateWindowEx(
		WS_EX_TOOLWINDOW,
		"dwm-bar",
		NULL, /* window title */
		WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 
		0, 0, 0, 0, 
		NULL, /* parent window */
		NULL, /* menu */
		hInstance,
		NULL
	);

	/* calculate width of the largest layout symbol */
	dc.hdc = GetWindowDC(barhwnd);
	HFONT font = (HFONT)GetStockObject(SYSTEM_FONT); 
	SelectObject(dc.hdc, font);

	for(blw = i = 0; LENGTH(layouts) > 1 && i < LENGTH(layouts); i++) {
 		w = TEXTW(layouts[i].symbol);
		blw = MAX(blw, w);
	}

	ReleaseDC(barhwnd, dc.hdc);

	PostMessage(barhwnd, WM_PAINT, 0, 0);
	updatebar();
}

void
showclientclassname(const Arg *arg) {
	MessageBox(NULL, getclientclassname(GetForegroundWindow()), "Window class", MB_OK);
}

void
showhide(Client *c) {
	if(!c)
		return;
	if (!ISVISIBLE(c))
		c->ignore = true;
	/* XXX: is the order of showing / hidding important?
	 *      if so then use recursion like dwm does
	 */
	setvisibility(c->hwnd, ISVISIBLE(c));
	showhide(c->snext);
}

void
spawn(const Arg *arg) {
	ShellExecute(NULL, NULL, ((char **)arg->v)[0], ((char **)arg->v)[1], NULL, SW_SHOWDEFAULT);
}

void
tag(const Arg *arg) {
	if(sel && arg->ui & TAGMASK) {
		sel->tags = arg->ui & TAGMASK;
		arrange();
	}
}

int
textnw(const char *text, unsigned int len) {
	SIZE size;
	GetTextExtentPoint(dc.hdc, text, len, &size);
	if (size.cx > 0)
		size.cx += textmargin;
	return size.cx;  
}


void
tile(void) {
	int x, y, h, w, mw;
	unsigned int i, n;
	Client *c;

	for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next), n++);
	if(n == 0)
		return;

	/* master */
	c = nexttiled(clients);
	mw = mfact * ww;
	resize(c, wx, wy, (n == 1 ? ww : mw) - 2 * c->bw, wh - 2 * c->bw);

	if(--n == 0)
		return;

	/* tile stack */
	x = (wx + mw > c->x + c->w) ? c->x + c->w + 2 * c->bw : wx + mw;
	y = wy;
	w = (wx + mw > c->x + c->w) ? wx + ww - x : ww - mw;
	h = wh / n;
	if(h < bh)
		h = wh;

	for(i = 0, c = nexttiled(c->next); c; c = nexttiled(c->next), i++) {
		resize(c, x, y, w - 2 * c->bw, /* remainder */ ((i + 1 == n)
		       ? wy + wh - y - 2 * c->bw : h - 2 * c->bw));
		if(h != wh)
			y = c->y + HEIGHT(c);
	}
}

void
togglebar(const Arg *arg) {
	showbar = !showbar;
	updategeom();
	updatebar();
	arrange();
}

void
toggleborder(const Arg *arg) {
	if (!sel)
		return;
	setborder(sel, !sel->border);
}

void
toggleexplorer(const Arg *arg) {
	HWND hwnd = FindWindow("Progman", "Program Manager");
	if (hwnd)
		setvisibility(hwnd, !IsWindowVisible(hwnd));

	hwnd = FindWindow("Shell_TrayWnd", NULL);
	if (hwnd)
		setvisibility(hwnd, !IsWindowVisible(hwnd));

	
	updategeom();
	updatebar();
	arrange();		
}


void
togglefloating(const Arg *arg) {
	if(!sel)
		return;
	sel->isfloating = !sel->isfloating || sel->isfixed;
	setborder(sel, sel->isfloating);
	if(sel->isfloating)
		resize(sel, sel->x, sel->y, sel->w, sel->h);
	arrange();
}

void
toggletag(const Arg *arg) {
	unsigned int mask;

	if (!sel)
		return;
	
	mask = sel->tags ^ (arg->ui & TAGMASK);
	if(mask) {
		sel->tags = mask;
		arrange();
	}
}

void
toggleview(const Arg *arg) {
	unsigned int mask = tagset[seltags] ^ (arg->ui & TAGMASK);

	if(mask) {
		tagset[seltags] = mask;
		arrange();
	}
}

void
unmanage(Client *c) {
	debug(" unmanage %s\n", getclienttitle(c->hwnd));
	setborder(c, true);
	detach(c);
	detachstack(c);
	if(sel == c)
		focus(NULL);
	free(c);
	arrange();
}

void
updatebar(void) {
	//debug("updatebar: %s  x: %d y: %d w: %d h: %d\n", showbar ? "show" : "hide" , 0, by, ww, bh);
	SetWindowPos(barhwnd, showbar ? HWND_TOPMOST : HWND_NOTOPMOST, 0, by, ww, bh, (showbar ? SWP_SHOWWINDOW : SWP_HIDEWINDOW) | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
	
}

void
updategeom(void) {
	RECT wa;
	HWND hwnd = FindWindow("Shell_TrayWnd", NULL);
	/* check if the windows taskbar is visible and adjust
	 * the workspace accordingly.
	 */
	if (hwnd && IsWindowVisible(hwnd)) {	
		SystemParametersInfo(SPI_GETWORKAREA, 0, &wa, 0);
		sx = wa.left;
		sy = wa.top;
		sw = wa.right - wa.left;
		sh = wa.bottom - wa.top;
	} else {
		sx = GetSystemMetrics(SM_XVIRTUALSCREEN);
		sy = GetSystemMetrics(SM_YVIRTUALSCREEN);
		sw = GetSystemMetrics(SM_CXVIRTUALSCREEN);
		sh = GetSystemMetrics(SM_CYVIRTUALSCREEN);
	}

	bh = 20; /* XXX: fixed value */

	/* window area geometry */
	wx = sx;
	wy = showbar && topbar ? sy + bh : sy;
	ww = sw;
	wh = showbar ? sh - bh : sh;
	/* bar position */
	by = showbar ? (topbar ? wy - bh : wy + wh) : -bh;
	debug("updategeom: %d x %d\n", ww, wh);
}

void
updatestatus() {
	drawbar();
}

void
view(const Arg *arg) {
	if((arg->ui & TAGMASK) == tagset[seltags])
		return;
	seltags ^= 1; /* toggle sel tagset */
	if(arg->ui & TAGMASK)
		tagset[seltags] = arg->ui & TAGMASK;
	arrange();
}

void
zoom(const Arg *arg) {
	Client *c = sel;

	if(!lt[sellt]->arrange || lt[sellt]->arrange == monocle || (sel && sel->isfloating))
		return;
	if(c == nexttiled(clients))
		if(!c || !(c = nexttiled(c->next)))
			return;
	detach(c);
	attach(c);
	focus(c);
	arrange();
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {	
	MSG msg;

	setup(hInstance);

	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	cleanup();

	return msg.wParam;
}