Reformat README as markdown
[dwm-win32.git] / dwm-win32.c
blob1c259316aa7d54443a71db4a690ce44e6a4d0730
1 /* See LICENSE file for copyright and license details.
3 * This is a port of the popular X11 window manager dwm to Microsoft Windows.
4 * It was originally started by Marc Andre Tanner <mat at brain-dump dot org>
6 * Each child of the root window is called a client. Clients are organized
7 * in a global linked client list, the focus history is remembered through
8 * a global stack list. Each client contains a bit array to indicate the
9 * tags of a client.
11 * Keys and tagging rules are organized as arrays and defined in config.h.
13 * To understand everything else, start reading main().
16 #define WIN32_LEAN_AND_MEAN
17 #define _WIN32_WINNT 0x0500
19 #include <windows.h>
20 #include <winuser.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <shellapi.h>
24 #include <stdbool.h>
26 #define NAME "dwm-win32" /* Used for window name/class */
28 /* macros */
29 #define ISVISIBLE(x) ((x)->tags & tagset[seltags])
30 #define ISFOCUSABLE(x) (!(x)->isminimized && ISVISIBLE(x) && IsWindowVisible((x)->hwnd))
31 #define LENGTH(x) (sizeof x / sizeof x[0])
32 #define MAX(a, b) ((a) > (b) ? (a) : (b))
33 #define MIN(a, b) ((a) < (b) ? (a) : (b))
34 #define MAXTAGLEN 16
35 #define WIDTH(x) ((x)->w + 2 * (x)->bw)
36 #define HEIGHT(x) ((x)->h + 2 * (x)->bw)
37 #define TAGMASK ((int)((1LL << LENGTH(tags)) - 1))
38 #define TEXTW(x) (textnw(x, strlen(x)))
39 #ifdef NDEBUG
40 # define debug(format, args...) do { } while(false)
41 #else
42 # define debug eprint
43 #endif
45 /* enums */
46 enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
47 enum { ColBorder, ColFG, ColBG, ColLast }; /* color */
48 enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle }; /* clicks */
50 typedef struct {
51 int x, y, w, h;
52 unsigned long norm[ColLast];
53 unsigned long sel[ColLast];
54 HDC hdc;
55 } DC; /* draw context */
57 DC dc;
59 typedef union {
60 int i;
61 unsigned int ui;
62 float f;
63 void *v;
64 } Arg;
66 typedef struct {
67 unsigned int click;
68 unsigned int button;
69 unsigned int key;
70 void (*func)(const Arg *arg);
71 const Arg arg;
72 } Button;
74 typedef struct Client Client;
75 struct Client {
76 HWND hwnd;
77 HWND parent;
78 HWND root;
79 DWORD threadid;
80 int x, y, w, h;
81 int bw; // XXX: useless?
82 unsigned int tags;
83 bool isminimized;
84 bool isfloating;
85 bool isalive;
86 bool ignore;
87 bool border;
88 bool wasvisible;
89 bool isfixed, isurgent; // XXX: useless?
90 Client *next;
91 Client *snext;
94 typedef struct {
95 unsigned int mod;
96 unsigned int key;
97 void (*func)(const Arg *);
98 const Arg arg;
99 } Key;
101 typedef struct {
102 const char *symbol;
103 void (*arrange)(void);
104 } Layout;
106 typedef struct {
107 const char *class;
108 const char *title;
109 unsigned int tags;
110 bool isfloating;
111 } Rule;
113 /* function declarations */
114 static void applyrules(Client *c);
115 static void arrange(void);
116 static void attach(Client *c);
117 static void attachstack(Client *c);
118 static void cleanup();
119 static void clearurgent(Client *c);
120 static void detach(Client *c);
121 static void detachstack(Client *c);
122 static void die(const char *errstr, ...);
123 static void drawbar(void);
124 static void drawsquare(bool filled, bool empty, bool invert, unsigned long col[ColLast]);
125 static void drawtext(const char *text, unsigned long col[ColLast], bool invert);
126 void drawborder(Client *c, COLORREF color);
127 void eprint(const char *errstr, ...);
128 static void focus(Client *c);
129 static void focusstack(const Arg *arg);
130 static Client *getclient(HWND hwnd);
131 LPSTR getclientclassname(HWND hwnd);
132 LPSTR getclienttitle(HWND hwnd);
133 HWND getroot(HWND hwnd);
134 static void grabkeys(HWND hwnd);
135 static void killclient(const Arg *arg);
136 static Client *manage(HWND hwnd);
137 static void monocle(void);
138 static Client *nextchild(Client *p, Client *c);
139 static Client *nexttiled(Client *c);
140 static void quit(const Arg *arg);
141 static void resize(Client *c, int x, int y, int w, int h);
142 static void restack(void);
143 static BOOL CALLBACK scan(HWND hwnd, LPARAM lParam);
144 static void setborder(Client *c, bool border);
145 static void setvisibility(HWND hwnd, bool visibility);
146 static void setlayout(const Arg *arg);
147 static void setmfact(const Arg *arg);
148 static void setup(HINSTANCE hInstance);
149 static void setupbar(HINSTANCE hInstance);
150 static void showclientclassname(const Arg *arg);
151 static void showhide(Client *c);
152 static void spawn(const Arg *arg);
153 static void tag(const Arg *arg);
154 static int textnw(const char *text, unsigned int len);
155 static void tile(void);
156 static void togglebar(const Arg *arg);
157 static void toggleborder(const Arg *arg);
158 static void toggleexplorer(const Arg *arg);
159 static void togglefloating(const Arg *arg);
160 static void toggletag(const Arg *arg);
161 static void toggleview(const Arg *arg);
162 static void unmanage(Client *c);
163 static void updatebar(void);
164 static void updategeom(void);
165 static void view(const Arg *arg);
166 static void zoom(const Arg *arg);
168 /* Shell hook stuff */
170 typedef BOOL (*RegisterShellHookWindowProc) (HWND);
171 RegisterShellHookWindowProc RegisterShellHookWindow;
173 /* XXX: should be in a system header, no? */
174 typedef struct {
175 HWND hwnd;
176 RECT rc;
177 } SHELLHOOKINFO, *LPSHELLHOOKINFO;
179 /* variables */
180 static HWND dwmhwnd, barhwnd;
181 static char stext[256];
182 static int sx, sy, sw, sh; /* X display screen geometry x, y, width, height */
183 static int by, bh, blw; /* bar geometry y, height and layout symbol width */
184 static int wx, wy, ww, wh; /* window area geometry x, y, width, height, bar excluded */
185 static unsigned int seltags, sellt;
187 static Client *clients = NULL;
188 static Client *sel = NULL;
189 static Client *stack = NULL;
190 static Layout *lt[] = { NULL, NULL };
191 static UINT shellhookid; /* Window Message id */
193 /* configuration, allows nested code to access above variables */
194 #include "config.h"
196 /* compile-time check if all tags fit into an unsigned int bit array. */
197 struct NumTags { char limitexceeded[sizeof(unsigned int) * 8 < LENGTH(tags) ? -1 : 1]; };
199 /* elements of the window whose color should be set to the values in the array below */
200 static int colorwinelements[] = { COLOR_ACTIVEBORDER, COLOR_INACTIVEBORDER };
201 static COLORREF colors[2][LENGTH(colorwinelements)] = {
202 { 0, 0 }, /* used to save the values before dwm started */
203 { selbordercolor, normbordercolor },
207 /* function implementations */
208 void
209 applyrules(Client *c) {
210 unsigned int i;
211 Rule *r;
213 /* rule matching */
214 for(i = 0; i < LENGTH(rules); i++) {
215 r = &rules[i];
216 if((!r->title || strstr(getclienttitle(c->hwnd), r->title))
217 && (!r->class || strstr(getclientclassname(c->hwnd), r->class))) {
218 c->isfloating = r->isfloating;
219 c->tags |= r->tags & TAGMASK ? r->tags & TAGMASK : tagset[seltags];
222 if(!c->tags)
223 c->tags = tagset[seltags];
226 void
227 arrange(void) {
228 showhide(stack);
229 focus(NULL);
230 if(lt[sellt]->arrange)
231 lt[sellt]->arrange();
232 restack();
235 void
236 attach(Client *c) {
237 c->next = clients;
238 clients = c;
241 void
242 attachstack(Client *c) {
243 c->snext = stack;
244 stack = c;
247 bool
248 istoolwindowof(Client *p, Client *c) {
249 debug(" istoolwindowof: %s\n", getclienttitle(p->hwnd));
250 debug(" floating: %d\n", c->isfloating);
251 debug(" root: %d == %d\n", p->root, c->root);
252 debug(" threadid: %d == %d\n", p->threadid, c->threadid);
253 return c->isfloating && (p->root == c->root || p->threadid == c->threadid);
256 void
257 buttonpress(unsigned int button, POINTS *point) {
258 unsigned int i, x, click;
259 Arg arg = {0};
261 /* XXX: hack */
262 dc.hdc = GetWindowDC(barhwnd);
264 i = x = 0;
266 do { x += TEXTW(tags[i]); } while(point->x >= x && ++i < LENGTH(tags));
267 if(i < LENGTH(tags)) {
268 click = ClkTagBar;
269 arg.ui = 1 << i;
271 else if(point->x < x + blw)
272 click = ClkLtSymbol;
273 else if(point->x > wx + ww - TEXTW(stext))
274 click = ClkStatusText;
275 else
276 click = ClkWinTitle;
278 if (GetKeyState(VK_SHIFT) < 0)
279 return;
281 for(i = 0; i < LENGTH(buttons); i++) {
282 if(click == buttons[i].click && buttons[i].func && buttons[i].button == button
283 && (!buttons[i].key || GetKeyState(buttons[i].key) < 0)) {
284 buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
285 break;
290 void
291 cleanup() {
292 int i;
293 Arg a = {.ui = ~0};
294 Layout foo = { "", NULL };
296 for(i = 0; i < LENGTH(keys); i++) {
297 UnregisterHotKey(dwmhwnd, i);
300 DeregisterShellHookWindow(dwmhwnd);
302 view(&a);
303 lt[sellt] = &foo;
304 while(stack)
305 unmanage(stack);
307 SetSysColors(LENGTH(colorwinelements), colorwinelements, colors[0]);
309 DestroyWindow(dwmhwnd);
312 void
313 clearurgent(Client *c) {
314 c->isurgent = false;
317 void
318 detach(Client *c) {
319 Client **tc;
321 for(tc = &clients; *tc && *tc != c; tc = &(*tc)->next);
322 *tc = c->next;
325 void
326 detachstack(Client *c) {
327 Client **tc;
329 for(tc = &stack; *tc && *tc != c; tc = &(*tc)->snext);
330 *tc = c->snext;
333 void
334 die(const char *errstr, ...) {
335 va_list ap;
337 va_start(ap, errstr);
338 vfprintf(stderr, errstr, ap);
339 va_end(ap);
340 cleanup();
341 exit(EXIT_FAILURE);
344 void
345 drawbar(void) {
346 dc.hdc = GetWindowDC(barhwnd);
348 dc.h = bh;
350 int x;
351 unsigned int i, occ = 0, urg = 0;
352 unsigned long *col;
353 Client *c;
355 for(c = clients; c; c = c->next) {
356 occ |= c->tags;
357 if(c->isurgent)
358 urg |= c->tags;
361 dc.x = 0;
362 for(i = 0; i < LENGTH(tags); i++) {
363 dc.w = TEXTW(tags[i]);
364 col = tagset[seltags] & 1 << i ? dc.sel : dc.norm;
365 drawtext(tags[i], col, urg & 1 << i);
366 drawsquare(sel && sel->tags & 1 << i, occ & 1 << i, urg & 1 << i, col);
367 dc.x += dc.w;
369 if(blw > 0) {
370 dc.w = blw;
371 drawtext(lt[sellt]->symbol, dc.norm, false);
372 x = dc.x + dc.w;
374 else
375 x = dc.x;
376 dc.w = TEXTW(stext);
377 dc.x = ww - dc.w;
378 if(dc.x < x) {
379 dc.x = x;
380 dc.w = ww - x;
382 drawtext(stext, dc.norm, false);
384 if((dc.w = dc.x - x) > bh) {
385 dc.x = x;
386 if(sel) {
387 drawtext(getclienttitle(sel->hwnd), dc.sel, false);
388 drawsquare(sel->isfixed, sel->isfloating, false, dc.sel);
390 else
391 drawtext(NULL, dc.norm, false);
394 ReleaseDC(barhwnd, dc.hdc);
397 void
398 drawsquare(bool filled, bool empty, bool invert, COLORREF col[ColLast]) {
399 static int size = 5;
400 RECT r = { .left = dc.x + 1, .top = dc.y + 1, .right = dc.x + size, .bottom = dc.y + size };
402 HBRUSH brush = CreateSolidBrush(col[invert ? ColBG : ColFG]);
403 SelectObject(dc.hdc, brush);
405 if(filled) {
406 FillRect(dc.hdc, &r, brush);
407 } else if(empty) {
408 FillRect(dc.hdc, &r, brush);
410 DeleteObject(brush);
413 void
414 drawtext(const char *text, COLORREF col[ColLast], bool invert) {
415 RECT r = { .left = dc.x, .top = dc.y, .right = dc.x + dc.w, .bottom = dc.y + dc.h };
417 HPEN pen = CreatePen(PS_SOLID, borderpx, selbordercolor);
418 HBRUSH brush = CreateSolidBrush(col[invert ? ColFG : ColBG]);
419 SelectObject(dc.hdc, pen);
420 SelectObject(dc.hdc, brush);
421 FillRect(dc.hdc, &r, brush);
423 DeleteObject(brush);
424 DeleteObject(pen);
426 SetBkMode(dc.hdc, TRANSPARENT);
427 SetTextColor(dc.hdc, col[invert ? ColBG : ColFG]);
429 HFONT font = (HFONT)GetStockObject(SYSTEM_FONT);
430 SelectObject(dc.hdc, font);
432 DrawText(dc.hdc, text, -1, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
435 void
436 eprint(const char *errstr, ...) {
437 va_list ap;
439 va_start(ap, errstr);
440 vfprintf(stderr, errstr, ap);
441 fflush(stderr);
442 va_end(ap);
445 void
446 setselected(Client *c) {
447 if(!c || !ISVISIBLE(c))
448 for(c = stack; c && !ISVISIBLE(c); c = c->snext);
449 if(sel && sel != c)
450 drawborder(sel, normbordercolor);
451 if(c) {
452 if(c->isurgent)
453 clearurgent(c);
454 detachstack(c);
455 attachstack(c);
456 drawborder(c, selbordercolor);
458 sel = c;
459 drawbar();
462 void
463 focus(Client *c) {
464 setselected(c);
465 if (sel)
466 SetForegroundWindow(sel->hwnd);
469 void
470 focusstack(const Arg *arg) {
471 Client *c = NULL, *i;
473 if(!sel)
474 return;
475 if (arg->i > 0) {
476 for(c = sel->next; c && !ISFOCUSABLE(c); c = c->next);
477 if(!c)
478 for(c = clients; c && !ISFOCUSABLE(c); c = c->next);
480 else {
481 for(i = clients; i != sel; i = i->next)
482 if(ISFOCUSABLE(i))
483 c = i;
484 if(!c)
485 for(; i; i = i->next)
486 if(ISFOCUSABLE(i))
487 c = i;
489 if(c) {
490 focus(c);
491 restack();
495 Client *
496 managechildwindows(Client *p) {
497 Client *c, *t;
498 EnumChildWindows(p->hwnd, scan, 0);
499 /* remove all child windows which were not part
500 * of the enumeration above.
502 for(c = clients; c; ) {
503 if (c->parent == p->hwnd) {
504 /* XXX: ismanageable isn't that reliable or some
505 * windows change over time which means they
506 * were once reported as manageable but not
507 * this time so we also check if they are
508 * currently visible and if that's the case
509 * we keep them in our client list.
511 if (!c->isalive && !IsWindowVisible(c->hwnd)) {
512 t = c->next;
513 unmanage(c);
514 c = t;
515 continue;
517 /* reset flag for next check */
518 c->isalive = false;
520 c = c->next;
523 return nextchild(p, clients);
526 Client *
527 getclient(HWND hwnd) {
528 Client *c;
530 for(c = clients; c; c = c->next)
531 if (c->hwnd == hwnd)
532 return c;
533 return NULL;
536 LPSTR
537 getclientclassname(HWND hwnd) {
538 static TCHAR buf[128];
539 GetClassName(hwnd, buf, sizeof buf);
540 return buf;
543 LPSTR
544 getclienttitle(HWND hwnd) {
545 static TCHAR buf[128];
546 GetWindowText(hwnd, buf, sizeof buf);
547 return buf;
550 HWND
551 getroot(HWND hwnd){
552 HWND parent, deskwnd = GetDesktopWindow();
554 while ((parent = GetWindow(hwnd, GW_OWNER)) != NULL && deskwnd != parent)
555 hwnd = parent;
557 return hwnd;
560 void
561 grabkeys(HWND hwnd) {
562 int i;
563 for (i = 0; i < LENGTH(keys); i++) {
564 RegisterHotKey(hwnd, i, keys[i].mod, keys[i].key);
568 bool
569 ismanageable(HWND hwnd){
570 if (getclient(hwnd))
571 return true;
573 HWND parent = GetParent(hwnd);
574 HWND owner = GetWindow(hwnd, GW_OWNER);
575 int style = GetWindowLong(hwnd, GWL_STYLE);
576 int exstyle = GetWindowLong(hwnd, GWL_EXSTYLE);
577 bool pok = (parent != 0 && ismanageable(parent));
578 bool istool = exstyle & WS_EX_TOOLWINDOW;
579 bool isapp = exstyle & WS_EX_APPWINDOW;
581 if (pok && !getclient(parent))
582 manage(parent);
584 debug("ismanageable: %s\n", getclienttitle(hwnd));
585 debug(" hwnd: %d\n", hwnd);
586 debug(" window: %d\n", IsWindow(hwnd));
587 debug(" visible: %d\n", IsWindowVisible(hwnd));
588 debug(" parent: %d\n", parent);
589 debug("parentok: %d\n", pok);
590 debug(" owner: %d\n", owner);
591 debug(" toolwin: %d\n", istool);
592 debug(" appwin: %d\n", isapp);
594 /* XXX: should we do this? */
595 if (GetWindowTextLength(hwnd) == 0) {
596 debug(" title: NULL\n");
597 debug(" manage: false\n");
598 return false;
601 if (style & WS_DISABLED) {
602 debug("disabled: true\n");
603 debug(" manage: false\n");
604 return false;
608 * WS_EX_APPWINDOW
609 * Forces a top-level window onto the taskbar when
610 * the window is visible.
612 * WS_EX_TOOLWINDOW
613 * Creates a tool window; that is, a window intended
614 * to be used as a floating toolbar. A tool window
615 * has a title bar that is shorter than a normal
616 * title bar, and the window title is drawn using
617 * a smaller font. A tool window does not appear in
618 * the taskbar or in the dialog that appears when
619 * the user presses ALT+TAB. If a tool window has
620 * a system menu, its icon is not displayed on the
621 * title bar. However, you can display the system
622 * menu by right-clicking or by typing ALT+SPACE.
625 if ((parent == 0 && IsWindowVisible(hwnd)) || pok) {
626 if ((!istool && parent == 0) || (istool && pok)) {
627 debug(" manage: true\n");
628 return true;
630 if (isapp && parent != 0) {
631 debug(" manage: true\n");
632 return true;
635 debug(" manage: false\n");
636 return false;
639 void
640 killclient(const Arg *arg) {
641 if(!sel)
642 return;
643 PostMessage(sel->hwnd, WM_CLOSE, 0, 0);
646 Client*
647 manage(HWND hwnd) {
648 static Client cz;
649 Client *c = getclient(hwnd);
651 if (c)
652 return c;
654 debug(" manage %s\n", getclienttitle(hwnd));
656 WINDOWINFO wi = {
657 .cbSize = sizeof(WINDOWINFO),
660 if (!GetWindowInfo(hwnd, &wi))
661 return NULL;
663 if(!(c = malloc(sizeof(Client))))
664 die("fatal: could not malloc() %u bytes\n", sizeof(Client));
666 *c = cz;
667 c->hwnd = hwnd;
668 c->threadid = GetWindowThreadProcessId(hwnd, NULL);
669 c->parent = GetParent(hwnd);
670 c->root = getroot(hwnd);
671 c->isalive = true;
673 static WINDOWPLACEMENT wp = {
674 .length = sizeof(WINDOWPLACEMENT),
675 .showCmd = SW_RESTORE,
678 if (IsWindowVisible(c->hwnd))
679 SetWindowPlacement(hwnd, &wp);
681 /* maybe we could also filter based on
682 * WS_MINIMIZEBOX and WS_MAXIMIZEBOX
684 c->isfloating = (wi.dwStyle & WS_POPUP) ||
685 (!(wi.dwStyle & WS_MINIMIZEBOX) && !(wi.dwStyle & WS_MAXIMIZEBOX));
687 debug(" window style: %d\n", wi.dwStyle);
688 debug(" minimize: %d\n", wi.dwStyle & WS_MINIMIZEBOX);
689 debug(" maximize: %d\n", wi.dwStyle & WS_MAXIMIZEBOX);
690 debug(" popup: %d\n", wi.dwStyle & WS_POPUP);
691 debug(" isfloating: %d\n", c->isfloating);
693 applyrules(c);
695 if (!c->isfloating)
696 setborder(c, false);
698 if (c->isfloating && IsWindowVisible(hwnd)) {
699 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);
700 resize(c, wi.rcWindow.left, wi.rcWindow.top, wi.rcWindow.right - wi.rcWindow.left, wi.rcWindow.bottom - wi.rcWindow.top);
703 attach(c);
704 attachstack(c);
705 return c;
708 void
709 monocle(void) {
710 Client *c;
712 for(c = nexttiled(clients); c; c = nexttiled(c->next)) {
713 resize(c, wx, wy, ww - 2 * c->bw, wh - 2 * c->bw);
717 Client *
718 nextchild(Client *p, Client *c) {
719 for(; c && c->parent != p->hwnd; c = c->next);
720 return c;
723 Client *
724 nexttiled(Client *c) {
725 for(; c && (c->isfloating || IsIconic(c->hwnd) || !ISVISIBLE(c) || !IsWindowVisible(c->hwnd)); c = c->next);
726 return c;
729 void
730 quit(const Arg *arg) {
731 PostMessage(dwmhwnd, WM_CLOSE, 0, 0);
734 void
735 resize(Client *c, int x, int y, int w, int h) {
736 if(w <= 0 && h <= 0) {
737 setvisibility(c->hwnd, false);
738 return;
740 if(x > sx + sw)
741 x = sw - WIDTH(c);
742 if(y > sy + sh)
743 y = sh - HEIGHT(c);
744 if(x + w + 2 * c->bw < sx)
745 x = sx;
746 if(y + h + 2 * c->bw < sy)
747 y = sy;
748 if(h < bh)
749 h = bh;
750 if(w < bh)
751 w = bh;
752 if(c->x != x || c->y != y || c->w != w || c->h != h) {
753 c->x = x;
754 c->y = y;
755 c->w = w;
756 c->h = h;
757 debug(" resize %d: %s: x: %d y: %d w: %d h: %d\n", c->hwnd, getclienttitle(c->hwnd), x, y, w, h);
758 SetWindowPos(c->hwnd, HWND_TOP, c->x, c->y, c->w, c->h, SWP_NOACTIVATE);
762 void
763 restack(void) {
767 LRESULT CALLBACK barhandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
769 switch (msg) {
770 case WM_CREATE:
771 updatebar();
772 break;
773 case WM_PAINT: {
774 PAINTSTRUCT ps;
775 BeginPaint(hwnd, &ps);
776 drawbar();
777 EndPaint(hwnd, &ps);
778 break;
780 case WM_LBUTTONDOWN:
781 case WM_RBUTTONDOWN:
782 case WM_MBUTTONDOWN:
783 buttonpress(msg, &MAKEPOINTS(lParam));
784 break;
785 default:
786 return DefWindowProc(hwnd, msg, wParam, lParam);
789 return 0;
792 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
794 switch (msg) {
795 case WM_CREATE:
796 break;
797 case WM_CLOSE:
798 cleanup();
799 break;
800 case WM_DESTROY:
801 PostQuitMessage(0);
802 break;
803 case WM_HOTKEY:
804 if (wParam > 0 && wParam < LENGTH(keys)) {
805 keys[wParam].func(&(keys[wParam ].arg));
807 break;
808 default:
809 if (msg == shellhookid) { /* Handle the shell hook message */
810 Client *c = getclient((HWND)lParam);
811 switch (wParam & 0x7fff) {
812 /* The first two events are also trigger if windows
813 * are being hidden or shown because of a tag
814 * switch, therefore we ignore them in this case.
816 case HSHELL_WINDOWCREATED:
817 debug("window created: %s\n", getclienttitle((HWND)lParam));
818 if (!c && ismanageable((HWND)lParam)) {
819 c = manage((HWND)lParam);
820 managechildwindows(c);
821 arrange();
823 break;
824 case HSHELL_WINDOWDESTROYED:
825 if (c) {
826 debug(" window %s: %s\n", c->ignore ? "hidden" : "destroyed", getclienttitle(c->hwnd));
827 if (!c->ignore)
828 unmanage(c);
829 else
830 c->ignore = false;
831 } else {
832 debug(" unmanaged window destroyed\n");
834 break;
835 case HSHELL_WINDOWACTIVATED:
836 debug(" window activated: %s || %d\n", c ? getclienttitle(c->hwnd) : "unknown", (HWND)lParam);
837 if (c) {
838 Client *t = sel;
839 managechildwindows(c);
840 setselected(c);
841 /* check if the previously selected
842 * window got minimized
844 if (t && (t->isminimized = IsIconic(t->hwnd))) {
845 debug(" active window got minimized: %s\n", getclienttitle(t->hwnd));
846 arrange();
848 /* the newly focused window was minimized */
849 if (sel->isminimized) {
850 debug(" newly active window was minimized: %s\n", getclienttitle(sel->hwnd));
851 sel->isminimized = false;
852 zoom(NULL);
854 } else {
855 /* Some window don't seem to generate
856 * HSHELL_WINDOWCREATED messages therefore
857 * we check here whether we should manage
858 * the window or not.
860 if (ismanageable((HWND)lParam)) {
861 c = manage((HWND)lParam);
862 managechildwindows(c);
863 setselected(c);
864 arrange();
867 break;
869 } else
870 return DefWindowProc(hwnd, msg, wParam, lParam);
873 return 0;
876 BOOL CALLBACK
877 scan(HWND hwnd, LPARAM lParam) {
878 Client *c = getclient(hwnd);
879 if (c)
880 c->isalive = true;
881 else if (ismanageable(hwnd))
882 manage(hwnd);
883 return TRUE;
886 void
887 drawborder(Client *c, COLORREF color) {
888 #if 0
889 HDC hdc = GetWindowDC(c->hwnd);
891 #if 0
892 /* this would be another way, but it uses standard sytem colors */
893 RECT area = { .left = 0, .top = 0, .right = c->w, .bottom = c->h };
894 DrawEdge(hdc, &area, BDR_RAISEDOUTER | BDR_SUNKENINNER, BF_RECT);
895 #else
896 HPEN pen = CreatePen(PS_SOLID, borderpx, color);
897 SelectObject(hdc, pen);
898 MoveToEx(hdc, 0, 0, NULL);
899 LineTo(hdc, c->w, 0);
900 LineTo(hdc, c->w, c->h);
901 LineTo(hdc, 0, c->h);
902 LineTo(hdc, 0, 0);
903 DeleteObject(pen);
904 #endif
906 ReleaseDC(c->hwnd, hdc);
907 #endif
910 void
911 setborder(Client *c, bool border) {
912 if (border) {
913 SetWindowLong(c->hwnd, GWL_STYLE, (GetWindowLong(c->hwnd, GWL_STYLE) | (WS_CAPTION | WS_SIZEBOX)));
914 } else {
915 /* XXX: ideally i would like to use the standard window border facilities and just modify the
916 * color with SetSysColor but this only seems to work if we leave WS_SIZEBOX enabled which
917 * is not optimal.
919 SetWindowLong(c->hwnd, GWL_STYLE, (GetWindowLong(c->hwnd, GWL_STYLE) & ~(WS_CAPTION | WS_SIZEBOX)) | WS_BORDER | WS_THICKFRAME);
920 SetWindowLong(c->hwnd, GWL_EXSTYLE, (GetWindowLong(c->hwnd, GWL_EXSTYLE) & ~(WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE)));
922 SetWindowPos(c->hwnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER );
923 c->border = border;
926 void
927 setvisibility(HWND hwnd, bool visibility) {
928 SetWindowPos(hwnd, 0, 0, 0, 0, 0, (visibility ? SWP_SHOWWINDOW : SWP_HIDEWINDOW) | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
931 void
932 setlayout(const Arg *arg) {
933 if(!arg || !arg->v || arg->v != lt[sellt])
934 sellt ^= 1;
935 if(arg && arg->v)
936 lt[sellt] = (Layout *)arg->v;
937 if(sel)
938 arrange();
939 else
940 drawbar();
943 /* arg > 1.0 will set mfact absolutly */
944 void
945 setmfact(const Arg *arg) {
946 float f;
948 if(!arg || !lt[sellt]->arrange)
949 return;
950 f = arg->f < 1.0 ? arg->f + mfact : arg->f - 1.0;
951 if(f < 0.1 || f > 0.9)
952 return;
953 mfact = f;
954 arrange();
957 void
958 setup(HINSTANCE hInstance) {
960 unsigned int i;
962 lt[0] = &layouts[0];
963 lt[1] = &layouts[1 % LENGTH(layouts)];
965 /* init appearance */
967 dc.norm[ColBorder] = normbordercolor;
968 dc.norm[ColBG] = normbgcolor;
969 dc.norm[ColFG] = normfgcolor;
970 dc.sel[ColBorder] = selbordercolor;
971 dc.sel[ColBG] = selbgcolor;
972 dc.sel[ColFG] = selfgcolor;
974 /* save colors so we can restore them in cleanup */
975 for (i = 0; i < LENGTH(colorwinelements); i++)
976 colors[0][i] = GetSysColor(colorwinelements[i]);
978 SetSysColors(LENGTH(colorwinelements), colorwinelements, colors[1]);
980 //SystemParametersInfo(SPI_SETBORDER, 1 /* width */, 0, 0);
982 updategeom();
984 WNDCLASSEX winClass;
986 winClass.cbSize = sizeof(WNDCLASSEX);
987 winClass.style = 0;
988 winClass.lpfnWndProc = WndProc;
989 winClass.cbClsExtra = 0;
990 winClass.cbWndExtra = 0;
991 winClass.hInstance = hInstance;
992 winClass.hIcon = NULL;
993 winClass.hIconSm = NULL;
994 winClass.hCursor = NULL;
995 winClass.hbrBackground = NULL;
996 winClass.lpszMenuName = NULL;
997 winClass.lpszClassName = NAME;
999 if (!RegisterClassEx(&winClass))
1000 die("Error registering window class");
1002 dwmhwnd = CreateWindowEx(0, NAME, NAME, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, hInstance, NULL);
1004 if (!dwmhwnd)
1005 die("Error creating window");
1007 grabkeys(dwmhwnd);
1009 EnumWindows(scan, 0);
1011 setupbar(hInstance);
1013 arrange();
1015 /* Get function pointer for RegisterShellHookWindow */
1016 RegisterShellHookWindow = (RegisterShellHookWindowProc)GetProcAddress(GetModuleHandle("USER32.DLL"), "RegisterShellHookWindow");
1017 if (!RegisterShellHookWindow)
1018 die("Could not find RegisterShellHookWindow");
1019 RegisterShellHookWindow(dwmhwnd);
1020 /* Grab a dynamic id for the SHELLHOOK message to be used later */
1021 shellhookid = RegisterWindowMessage("SHELLHOOK");
1024 void
1025 setupbar(HINSTANCE hInstance) {
1027 unsigned int i, w = 0;
1029 WNDCLASS winClass;
1030 memset(&winClass, 0, sizeof winClass);
1032 winClass.style = 0;
1033 winClass.lpfnWndProc = barhandler;
1034 winClass.cbClsExtra = 0;
1035 winClass.cbWndExtra = 0;
1036 winClass.hInstance = hInstance;
1037 winClass.hIcon = NULL;
1038 winClass.hCursor = LoadCursor(NULL, IDC_ARROW);
1039 winClass.hbrBackground = NULL;
1040 winClass.lpszMenuName = NULL;
1041 winClass.lpszClassName = "dwm-bar";
1043 if (!RegisterClass(&winClass))
1044 die("Error registering window class");
1046 barhwnd = CreateWindowEx(
1047 WS_EX_TOOLWINDOW,
1048 "dwm-bar",
1049 NULL, /* window title */
1050 WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
1051 0, 0, 0, 0,
1052 NULL, /* parent window */
1053 NULL, /* menu */
1054 hInstance,
1055 NULL
1058 /* calculate width of the largest layout symbol */
1059 dc.hdc = GetWindowDC(barhwnd);
1060 HFONT font = (HFONT)GetStockObject(SYSTEM_FONT);
1061 SelectObject(dc.hdc, font);
1063 for(blw = i = 0; LENGTH(layouts) > 1 && i < LENGTH(layouts); i++) {
1064 w = TEXTW(layouts[i].symbol);
1065 blw = MAX(blw, w);
1068 ReleaseDC(barhwnd, dc.hdc);
1070 PostMessage(barhwnd, WM_PAINT, 0, 0);
1071 updatebar();
1074 void
1075 showclientclassname(const Arg *arg) {
1076 MessageBox(NULL, getclientclassname(GetForegroundWindow()), "Window class", MB_OK);
1079 void
1080 showhide(Client *c) {
1081 if(!c)
1082 return;
1083 /* XXX: is the order of showing / hidding important? */
1084 if (!ISVISIBLE(c)) {
1085 if (IsWindowVisible(c->hwnd)) {
1086 c->ignore = true;
1087 c->wasvisible = true;
1088 setvisibility(c->hwnd, false);
1090 } else {
1091 if (c->wasvisible) {
1092 setvisibility(c->hwnd, true);
1095 showhide(c->snext);
1098 void
1099 spawn(const Arg *arg) {
1100 ShellExecute(NULL, NULL, ((char **)arg->v)[0], ((char **)arg->v)[1], NULL, SW_SHOWDEFAULT);
1103 void
1104 tag(const Arg *arg) {
1105 Client *c;
1107 if(sel && arg->ui & TAGMASK) {
1108 sel->tags = arg->ui & TAGMASK;
1109 debug("window tagged: %d %s\n", sel->hwnd, getclienttitle(sel->hwnd));
1110 for (c = managechildwindows(sel); c; c = nextchild(sel, c->next)) {
1111 debug(" child window which is %s tagged: %s\n", c->isfloating ? "floating" : "normal", getclienttitle(c->hwnd));
1112 if (c->isfloating)
1113 c->tags = arg->ui & TAGMASK;
1115 debug("window tagged finished\n");
1116 arrange();
1121 textnw(const char *text, unsigned int len) {
1122 SIZE size;
1123 GetTextExtentPoint(dc.hdc, text, len, &size);
1124 if (size.cx > 0)
1125 size.cx += textmargin;
1126 return size.cx;
1130 void
1131 tile(void) {
1132 int x, y, h, w, mw;
1133 unsigned int i, n;
1134 Client *c;
1136 for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next), n++);
1137 if(n == 0)
1138 return;
1140 /* master */
1141 c = nexttiled(clients);
1142 mw = mfact * ww;
1143 resize(c, wx, wy, (n == 1 ? ww : mw) - 2 * c->bw, wh - 2 * c->bw);
1145 if(--n == 0)
1146 return;
1148 /* tile stack */
1149 x = (wx + mw > c->x + c->w) ? c->x + c->w + 2 * c->bw : wx + mw;
1150 y = wy;
1151 w = (wx + mw > c->x + c->w) ? wx + ww - x : ww - mw;
1152 h = wh / n;
1153 if(h < bh)
1154 h = wh;
1156 for(i = 0, c = nexttiled(c->next); c; c = nexttiled(c->next), i++) {
1157 resize(c, x, y, w - 2 * c->bw, /* remainder */ ((i + 1 == n)
1158 ? wy + wh - y - 2 * c->bw : h - 2 * c->bw));
1159 if(h != wh)
1160 y = c->y + HEIGHT(c);
1164 void
1165 togglebar(const Arg *arg) {
1166 showbar = !showbar;
1167 updategeom();
1168 updatebar();
1169 arrange();
1172 void
1173 toggleborder(const Arg *arg) {
1174 if (!sel)
1175 return;
1176 setborder(sel, !sel->border);
1179 void
1180 toggleexplorer(const Arg *arg) {
1181 HWND hwnd = FindWindow("Progman", "Program Manager");
1182 if (hwnd)
1183 setvisibility(hwnd, !IsWindowVisible(hwnd));
1185 hwnd = FindWindow("Shell_TrayWnd", NULL);
1186 if (hwnd)
1187 setvisibility(hwnd, !IsWindowVisible(hwnd));
1190 updategeom();
1191 updatebar();
1192 arrange();
1196 void
1197 togglefloating(const Arg *arg) {
1198 if(!sel)
1199 return;
1200 sel->isfloating = !sel->isfloating || sel->isfixed;
1201 setborder(sel, sel->isfloating);
1202 if(sel->isfloating)
1203 resize(sel, sel->x, sel->y, sel->w, sel->h);
1204 arrange();
1207 void
1208 toggletag(const Arg *arg) {
1209 unsigned int mask;
1211 if (!sel)
1212 return;
1214 mask = sel->tags ^ (arg->ui & TAGMASK);
1215 if(mask) {
1216 sel->tags = mask;
1217 arrange();
1221 void
1222 toggleview(const Arg *arg) {
1223 unsigned int mask = tagset[seltags] ^ (arg->ui & TAGMASK);
1225 if(mask) {
1226 tagset[seltags] = mask;
1227 arrange();
1231 void
1232 unmanage(Client *c) {
1233 debug(" unmanage %s\n", getclienttitle(c->hwnd));
1234 if (c->wasvisible)
1235 setvisibility(c->hwnd, true);
1236 if (!c->isfloating)
1237 setborder(c, true);
1238 detach(c);
1239 detachstack(c);
1240 if(sel == c)
1241 focus(NULL);
1242 free(c);
1243 arrange();
1246 void
1247 updatebar(void) {
1248 SetWindowPos(barhwnd, showbar ? HWND_TOPMOST : HWND_NOTOPMOST, 0, by, ww, bh, (showbar ? SWP_SHOWWINDOW : SWP_HIDEWINDOW) | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
1251 void
1252 updategeom(void) {
1253 RECT wa;
1254 HWND hwnd = FindWindow("Shell_TrayWnd", NULL);
1255 /* check if the windows taskbar is visible and adjust
1256 * the workspace accordingly.
1258 if (hwnd && IsWindowVisible(hwnd)) {
1259 SystemParametersInfo(SPI_GETWORKAREA, 0, &wa, 0);
1260 sx = wa.left;
1261 sy = wa.top;
1262 sw = wa.right - wa.left;
1263 sh = wa.bottom - wa.top;
1264 } else {
1265 sx = GetSystemMetrics(SM_XVIRTUALSCREEN);
1266 sy = GetSystemMetrics(SM_YVIRTUALSCREEN);
1267 sw = GetSystemMetrics(SM_CXVIRTUALSCREEN);
1268 sh = GetSystemMetrics(SM_CYVIRTUALSCREEN);
1271 bh = 20; /* XXX: fixed value */
1273 /* window area geometry */
1274 wx = sx;
1275 wy = showbar && topbar ? sy + bh : sy;
1276 ww = sw;
1277 wh = showbar ? sh - bh : sh;
1278 /* bar position */
1279 by = showbar ? (topbar ? wy - bh : wy + wh) : -bh;
1280 debug("updategeom: %d x %d\n", ww, wh);
1283 void
1284 view(const Arg *arg) {
1285 if((arg->ui & TAGMASK) == tagset[seltags])
1286 return;
1287 seltags ^= 1; /* toggle sel tagset */
1288 if(arg->ui & TAGMASK)
1289 tagset[seltags] = arg->ui & TAGMASK;
1290 arrange();
1293 void
1294 zoom(const Arg *arg) {
1295 Client *c = sel;
1297 if(!lt[sellt]->arrange || lt[sellt]->arrange == monocle || (sel && sel->isfloating))
1298 return;
1299 if(c == nexttiled(clients))
1300 if(!c || !(c = nexttiled(c->next)))
1301 return;
1302 detach(c);
1303 attach(c);
1304 focus(c);
1305 arrange();
1308 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
1309 MSG msg;
1311 setup(hInstance);
1313 while (GetMessage(&msg, NULL, 0, 0) > 0) {
1314 TranslateMessage(&msg);
1315 DispatchMessage(&msg);
1318 cleanup();
1320 return msg.wParam;