├── LICENSE.txt ├── Makefile ├── README.txt ├── config.def.h ├── config.mk └── dwm-win32.c /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT/X Consortium License 2 | 3 | © 2006-2009 Anselm R Garbe 4 | © 2006-2007 Sander van Dijk 5 | © 2006-2007 Jukka Salmi 6 | © 2007 Premysl Hruby 7 | © 2007 Szabolcs Nagy 8 | © 2007 Christof Musik 9 | © 2007-2008 Enno Gottox Boland 10 | © 2007-2008 Peter Hartlich 11 | © 2008 Martin Hurton 12 | © 2008 Neale Pickett 13 | © 2009 Marc Andre Tanner 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a 16 | copy of this software and associated documentation files (the "Software"), 17 | to deal in the Software without restriction, including without limitation 18 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 19 | and/or sell copies of the Software, and to permit persons to whom the 20 | Software is furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in 23 | all copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 28 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 30 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 31 | DEALINGS IN THE SOFTWARE. 32 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # dwm-win32 - dynamic window manager for win32 2 | # See LICENSE file for copyright and license details. 3 | 4 | include config.mk 5 | 6 | SRC = dwm-win32.c 7 | OBJ = ${SRC:.c=.o} 8 | 9 | all: options dwm-win32 10 | 11 | options: 12 | @echo dwm-win32 build options: 13 | @echo "CFLAGS = ${CFLAGS}" 14 | @echo "LDFLAGS = ${LDFLAGS}" 15 | @echo "CC = ${CC}" 16 | 17 | .c.o: 18 | @echo CC $< 19 | @${CC} -c ${CFLAGS} $< 20 | 21 | ${OBJ}: config.h config.mk 22 | 23 | config.h: 24 | @echo creating $@ from config.def.h 25 | @cp config.def.h $@ 26 | 27 | dwm-win32: ${OBJ} 28 | @echo CC -o $@ 29 | @${CC} -o $@ ${OBJ} ${LDFLAGS} 30 | 31 | clean: 32 | @echo cleaning 33 | @rm -f dwm-win32.exe ${OBJ} dwm-win32-${VERSION}.tar.gz 34 | 35 | dist: clean 36 | @echo creating dist tarball 37 | @mkdir -p dwm-win32-${VERSION} 38 | @cp -R LICENSE.txt Makefile README.txt config.def.h config.mk \ 39 | ${SRC} dwm-win32-${VERSION} 40 | @tar -cf dwm-win32-${VERSION}.tar dwm-win32-${VERSION} 41 | @gzip dwm-win32-${VERSION}.tar 42 | @rm -rf dwm-win32-${VERSION} 43 | 44 | install: all 45 | @echo installing executable file to ${DESTDIR}${PREFIX}/bin 46 | @mkdir -p ${DESTDIR}${PREFIX}/bin 47 | @cp -f dwm-win32 ${DESTDIR}${PREFIX}/bin 48 | @chmod 755 ${DESTDIR}${PREFIX}/bin/dwm-win32 49 | @echo installing manual page to ${DESTDIR}${MANPREFIX}/man1 50 | @mkdir -p ${DESTDIR}${MANPREFIX}/man1 51 | @sed "s/VERSION/${VERSION}/g" < dwm-win32.1 > ${DESTDIR}${MANPREFIX}/man1/dwm-win32.1 52 | @chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm-win32.1 53 | 54 | uninstall: 55 | @echo removing executable file from ${DESTDIR}${PREFIX}/bin 56 | @rm -f ${DESTDIR}${PREFIX}/bin/dwm-win32 57 | @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 58 | @rm -f ${DESTDIR}${MANPREFIX}/man1/dwm-win32.1 59 | 60 | .PHONY: all options clean dist install uninstall 61 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | dwm-win32 is a port of the well known X11 window manager dwm to Microsoft Windows. 2 | 3 | Description 4 | =========== 5 | 6 | dwm is a dynamic window manager for Microsoft Windows. It manages windows in tiled, 7 | monocle and floating layouts. Either layout can be applied dynamically, optimising 8 | the environment for the application in use and the task performed. 9 | 10 | In tiled layouts windows are managed in a master and stacking area. The master area 11 | contains the window which currently needs most attention, whereas the stacking area 12 | contains all other windows. In monocle layout all windows are maximised to the screen 13 | size. In floating layout windows can be resized and moved freely. Dialog windows are 14 | always managed floating, regardless of the layout applied. 15 | 16 | Windows are grouped by tags. Each window can be tagged with one or multiple tags. 17 | Selecting certain tags displays all windows with these tags. 18 | 19 | dwm contains a small status bar which displays all available tags, the layout, the 20 | title of the focused window, and the text read from the root window name property. 21 | A floating window is indicated with an empty square and a maximised floating window 22 | is indicated with a filled square before the windows title. The selected tags are 23 | indicated with a different color. The tags of the focused window are indicated with 24 | a filled square in the top left corner. The tags which are applied to one or more 25 | windows are indicated with an empty square in the top left corner. 26 | 27 | dwm draws a small border around windows to indicate the focus state. 28 | 29 | 30 | Usage 31 | ===== 32 | 33 | Keyboard 34 | 35 | dwm uses a modifier key by default this is CTRL + ALT. 36 | 37 | MOD + Shift + Return 38 | - start cmd.exe. 39 | 40 | MOD + b 41 | - Toggles bar on and off. 42 | 43 | MOD + e 44 | - Toogles windows explorer and taskbar on and off. 45 | 46 | MOD + c 47 | - Toogles clock on and off. 48 | 49 | MOD + t 50 | - Sets tiled layout. 51 | 52 | MOD + f 53 | - Sets floating layout. 54 | 55 | MOD + m 56 | - Sets monocle layout. 57 | 58 | MOD + y 59 | - Sets horizontal tiled layout. 60 | 61 | MOD + space 62 | - Toggles between current and previous layout. 63 | 64 | MOD + j 65 | - Focus next window. 66 | 67 | MOD + k 68 | - Focus previous window. 69 | 70 | MOD + h 71 | - Decrease master area size. 72 | 73 | MOD + l 74 | - Increase master area size. 75 | 76 | MOD + Shift + h 77 | - Increase master area window. 78 | 79 | MOD + Shift + l 80 | - Increase master area window. 81 | 82 | MOD + Control + h 83 | - Decrease master area window. 84 | 85 | MOD + Control + l 86 | - Decrease master area window. 87 | 88 | MOD + Return 89 | - Zooms/cycles focused window to/from master area (tiled layouts only). 90 | 91 | MOD + Shift + j 92 | - Switch focused window with the next one. 93 | 94 | MOD + Shift + k 95 | - Switch focused window with the prevoius one. 96 | 97 | MOD + Shift + c 98 | - Close focused window. 99 | 100 | MOD + Shift + Space 101 | - Toggle focused window between tiled and floating state. 102 | 103 | MOD + n 104 | - Toggles border of currently focused window. 105 | 106 | Mod + i 107 | - Display classname of currently focused window, useful for wiriting tagging rules. 108 | 109 | MOD + Tab 110 | - Toggles to the previously selected tags. 111 | 112 | MOD + Shift + [1..n] 113 | - Apply nth tag to focused window. 114 | 115 | MOD + Shift + 0 116 | - Apply all tags to focused window. 117 | 118 | MOD + Control + Shift + [1..n] 119 | - Add/remove nth tag to/from focused window. 120 | 121 | MOD + [1..n] 122 | - View all windows with nth tag. 123 | 124 | MOD + 0 125 | - View all windows with any tag. 126 | 127 | MOD + Control + [1..n] 128 | - Add/remove all windows with nth tag to/from the view. 129 | 130 | MOD + q 131 | - Quit dwm. 132 | 133 | 134 | Mouse 135 | 136 | Left Button 137 | - click on a tag label to display all windows with that tag, click on the layout 138 | label toggles between tiled and floating layout. 139 | 140 | Right Button 141 | - click on a tag label adds/removes all windows with that tag to/from the view. 142 | 143 | Alt + Left Button 144 | - click on a tag label applies that tag to the focused window. 145 | 146 | Alt + Right Button 147 | - click on a tag label adds/removes that tag to/from the focused window. 148 | 149 | 150 | How it works 151 | ============ 152 | 153 | A ShellHook is registered which is notified upon window creation and destruction, 154 | however it is important to know that this only works for unowned top level windows. 155 | This means we will not get notified when child windows are created/destroyed. 156 | Therefore we scan the currently active top level window upon activation to collect 157 | all associated child windows. This information is for example used to tag all windows 158 | and not just the toplevel one when tag changes occur. 159 | 160 | This is all kind of messy and we might miss some child windows in certain situations. 161 | A better approach would probably be to introduce a CBTProc function and register it 162 | with SetWindowsHookEx(WH_CBT, ...) with this we would get notified by all and every 163 | windows including toolbars etc. which we would have to filter out. 164 | 165 | Unfortunately the SetWindowsHookEx thingy seems to require a separate dll because it 166 | will be loaded into each process address space. 167 | 168 | TODO 169 | ==== 170 | - show/hide child windows upon tag switch, in theory this should already work but 171 | in practice we need to tweak ismanageable() so that it recognises child windows 172 | but doesn't generate false positives. 173 | - fullscreen windows, mstsc for example doesn't resize properly when maximized. 174 | - Screensaver? 175 | - system dialogs from desktop window 176 | - urgent flag? 177 | - window border isn't yet perfect 178 | - status text via stdin or a separate tool 179 | - crash handler which makes all windows visible restores borders etc 180 | - use BeginDeferWindowPos, DeferWindowPos and EndDeferWindowPos 181 | - optimize for speed 182 | - code cleanups all over the place 183 | - multi head support? 184 | 185 | [ - introduce a CBTProc function and register it with SetWindowsHookEx(WH_CBT, ...) 186 | to handle window events instead of the current mechanism in WndProc which 187 | is based on the shellhookid and WH_SHELL because this only works for 188 | toplevel windows. See also the "How it works" section. ] 189 | -------------------------------------------------------------------------------- /config.def.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | /* appearance, colors are specified in the form 0x00bbggrr or with the RGB(r, g, b) macro */ 4 | #define normbordercolor 0x00cccccc 5 | #define normbgcolor 0x00cccccc 6 | #define normfgcolor 0x00000000 7 | #define selbordercolor 0x00ff6600 8 | #define selbgcolor 0x00ff6600 9 | #define selfgcolor 0x00ffffff 10 | 11 | static const unsigned int borderpx = 2; /* border pixel of windows */ 12 | static const unsigned int textmargin = 5; /* margin for the text displayed on the bar */ 13 | static bool showbar = true; /* false means no bar */ 14 | static bool topbar = true; /* false means bottom bar */ 15 | static bool showclock = true; /* false means no clock */ 16 | 17 | /* tagging */ 18 | static const char tags[][MAXTAGLEN] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; 19 | static unsigned int tagset[] = {1, 1}; /* after start, first tag is selected */ 20 | 21 | static Rule rules[] = { 22 | /* class title tags mask isfloating */ 23 | { "MozillaUIWindowClass", "- Mozilla Firefox", 1 << 8, false }, 24 | }; 25 | 26 | /* layout(s) */ 27 | static float mfact = 0.55; /* factor of master area size [0.05..0.95] */ 28 | static unsigned int mcount = 1; /* windows of master area [1,] */ 29 | static unsigned int nmcolumn = 1; /* columns of non-master area [1,] */ 30 | 31 | static Layout layouts[] = { 32 | /* symbol arrange function */ 33 | { "[]=", tile }, /* first entry is default */ 34 | { "><>", NULL }, /* no layout function means floating behavior */ 35 | { "[M]", monocle }, 36 | { "|=|", htile}, /* 'horizontal' tile */ 37 | }; 38 | 39 | /* key definitions */ 40 | #define MODKEY (MOD_CONTROL | MOD_ALT) 41 | #define TAGKEYS(KEY,TAG) \ 42 | { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ 43 | { MODKEY|MOD_CONTROL, KEY, toggleview, {.ui = 1 << TAG} }, \ 44 | { MODKEY|MOD_SHIFT, KEY, tag, {.ui = 1 << TAG} }, \ 45 | { MODKEY|MOD_CONTROL|MOD_SHIFT, KEY, toggletag, {.ui = 1 << TAG} }, 46 | 47 | /* helper for spawning shell commands in the pre dwm-5.0 fashion */ 48 | #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } 49 | 50 | /* commands */ 51 | static const char *termcmd[] = { "cmd.exe", NULL }; 52 | 53 | static Key keys[] = { 54 | /* modifier key function argument */ 55 | { MODKEY|MOD_SHIFT, VK_RETURN, spawn, {.v = termcmd } }, 56 | { MODKEY, 'B', togglebar, {0} }, 57 | { MODKEY, 'J', focusstack, {.i = +1 } }, 58 | { MODKEY, 'K', focusstack, {.i = -1 } }, 59 | { MODKEY, 'H', setmfact, {.f = -0.05} }, 60 | { MODKEY, 'L', setmfact, {.f = +0.05} }, 61 | { MODKEY|MOD_SHIFT, 'H', setmcount, {.i = +1} }, 62 | { MODKEY|MOD_SHIFT, 'L', setmcount, {.i = -1} }, 63 | { MODKEY|MOD_CONTROL, 'H', setnmcolumn, {.i = +1} }, 64 | { MODKEY|MOD_CONTROL, 'L', setnmcolumn, {.i = -1} }, 65 | { MODKEY, 'I', showclientclassname, {0} }, 66 | { MODKEY, VK_RETURN, zoom, {0} }, 67 | { MODKEY|MOD_SHIFT, 'J', movetonext, {0} }, 68 | { MODKEY|MOD_SHIFT, 'K', movetoprev, {0} }, 69 | { MODKEY, VK_TAB, view, {0} }, 70 | { MODKEY|MOD_SHIFT, 'C', killclient, {0} }, 71 | { MODKEY, 'T', setlayout, {.v = &layouts[0]} }, 72 | { MODKEY, 'F', setlayout, {.v = &layouts[1]} }, 73 | { MODKEY, 'M', setlayout, {.v = &layouts[2]} }, 74 | { MODKEY, 'Y', setlayout, {.v = &layouts[3]} }, 75 | { MODKEY, VK_SPACE, setlayout, {0} }, 76 | { MODKEY|MOD_SHIFT, VK_SPACE, togglefloating, {0} }, 77 | { MODKEY, 'N', toggleborder, {0} }, 78 | { MODKEY, 'E', toggleexplorer, {0} }, 79 | { MODKEY, '0', view, {.ui = ~0 } }, 80 | { MODKEY|MOD_SHIFT, '0', tag, {.ui = ~0 } }, 81 | { MODKEY, 'C', toggleclock, {0} }, 82 | TAGKEYS( '1', 0) 83 | TAGKEYS( '2', 1) 84 | TAGKEYS( '3', 2) 85 | TAGKEYS( '4', 3) 86 | TAGKEYS( '5', 4) 87 | TAGKEYS( '6', 5) 88 | TAGKEYS( '7', 6) 89 | TAGKEYS( '8', 7) 90 | TAGKEYS( '9', 8) 91 | { MODKEY, 'Q', quit, {0} }, 92 | }; 93 | 94 | 95 | /* button definitions */ 96 | /* click can be a tag number (starting at 0), ClkLtSymbol, ClkStatusText or ClkWinTitle */ 97 | static Button buttons[] = { 98 | /* click button event type modifier keys function argument */ 99 | { ClkLtSymbol, WM_LBUTTONDOWN, 0, setlayout, {0} }, 100 | { ClkLtSymbol, WM_RBUTTONDOWN, 0, setlayout, {.v = &layouts[2]} }, 101 | { ClkWinTitle, WM_MBUTTONDOWN, 0, zoom, {0} }, 102 | { ClkStatusText, WM_MBUTTONDOWN, 0, spawn, {.v = termcmd } }, 103 | #if 0 104 | { ClkClientWin, WM_MBUTTONDOWN, MODKEY, togglefloating, {0} }, 105 | #endif 106 | { ClkTagBar, WM_LBUTTONDOWN, VK_MENU, tag, {0} }, 107 | { ClkTagBar, WM_RBUTTONDOWN, VK_MENU, toggletag, {0} }, 108 | { ClkTagBar, WM_LBUTTONDOWN, 0, view, {0} }, 109 | { ClkTagBar, WM_RBUTTONDOWN, 0, toggleview, {0} }, 110 | }; 111 | 112 | static char clockfmt[] = "%Y/%m/%d(%a) %H:%M"; 113 | static int clock_intval = 15000; 114 | -------------------------------------------------------------------------------- /config.mk: -------------------------------------------------------------------------------- 1 | # dwm-win32 version 2 | VERSION = alpha2 3 | 4 | # Customize below to fit your system 5 | 6 | # paths 7 | PREFIX = /usr/local 8 | MANPREFIX = ${PREFIX}/share/man 9 | 10 | # flags 11 | CPPFLAGS = -DVERSION=\"${VERSION}\" 12 | CFLAGS = -std=c99 -pedantic -Wall -Os ${CPPFLAGS} 13 | LDFLAGS = -s -mwindows 14 | 15 | # compiler and linker 16 | CC = gcc 17 | -------------------------------------------------------------------------------- /dwm-win32.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. 2 | * 3 | * This is a port of the popular X11 window manager dwm to Microsoft Windows. 4 | * It was originally started by Marc Andre Tanner 5 | * 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. 10 | * 11 | * Keys and tagging rules are organized as arrays and defined in config.h. 12 | * 13 | * To understand everything else, start reading main(). 14 | */ 15 | 16 | #define WIN32_LEAN_AND_MEAN 17 | #define _WIN32_WINNT 0x0500 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #define NAME "dwm-win32" /* Used for window name/class */ 28 | 29 | /* macros */ 30 | #define ISVISIBLE(x) ((x)->tags & tagset[seltags]) 31 | #define ISFOCUSABLE(x) (!(x)->isminimized && ISVISIBLE(x) && IsWindowVisible((x)->hwnd)) 32 | #define LENGTH(x) (sizeof x / sizeof x[0]) 33 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 34 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 35 | #define MAXTAGLEN 16 36 | #define WIDTH(x) ((x)->w + 2 * (x)->bw) 37 | #define HEIGHT(x) ((x)->h + 2 * (x)->bw) 38 | #define TAGMASK ((int)((1LL << LENGTH(tags)) - 1)) 39 | #define TEXTW(x) (textnw(x, strlen(x))) 40 | #ifdef NDEBUG 41 | # define debug(format, args...) do { } while(false) 42 | #else 43 | # define debug eprint 44 | #endif 45 | 46 | /* enums */ 47 | enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ 48 | enum { ColBorder, ColFG, ColBG, ColLast }; /* color */ 49 | enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle }; /* clicks */ 50 | 51 | typedef struct { 52 | int x, y, w, h; 53 | unsigned long norm[ColLast]; 54 | unsigned long sel[ColLast]; 55 | HDC hdc; 56 | } DC; /* draw context */ 57 | 58 | DC dc; 59 | 60 | typedef union { 61 | int i; 62 | unsigned int ui; 63 | float f; 64 | void *v; 65 | } Arg; 66 | 67 | typedef struct { 68 | unsigned int click; 69 | unsigned int button; 70 | unsigned int key; 71 | void (*func)(const Arg *arg); 72 | const Arg arg; 73 | } Button; 74 | 75 | typedef struct Client Client; 76 | struct Client { 77 | HWND hwnd; 78 | HWND parent; 79 | HWND root; 80 | DWORD threadid; 81 | int x, y, w, h; 82 | int bw; // XXX: useless? 83 | unsigned int tags; 84 | bool isminimized; 85 | bool isfloating; 86 | bool isalive; 87 | bool ignore; 88 | bool border; 89 | bool wasvisible; 90 | bool isfixed, isurgent; // XXX: useless? 91 | Client *next; 92 | Client *snext; 93 | }; 94 | 95 | typedef struct { 96 | unsigned int mod; 97 | unsigned int key; 98 | void (*func)(const Arg *); 99 | const Arg arg; 100 | } Key; 101 | 102 | typedef struct { 103 | const char *symbol; 104 | void (*arrange)(void); 105 | } Layout; 106 | 107 | typedef struct { 108 | const char *class; 109 | const char *title; 110 | unsigned int tags; 111 | bool isfloating; 112 | } Rule; 113 | 114 | /* function declarations */ 115 | static void applyrules(Client *c); 116 | static void arrange(void); 117 | static void attach(Client *c); 118 | static void attachstack(Client *c); 119 | static void cleanup(); 120 | static void clearurgent(Client *c); 121 | static void detach(Client *c); 122 | static void detachstack(Client *c); 123 | static void die(const char *errstr, ...); 124 | static void drawbar(void); 125 | static void drawsquare(bool filled, bool empty, bool invert, unsigned long col[ColLast]); 126 | static void drawtext(const char *text, unsigned long col[ColLast], bool invert); 127 | void drawborder(Client *c, COLORREF color); 128 | void eprint(const char *errstr, ...); 129 | static void focus(Client *c); 130 | static void focusstack(const Arg *arg); 131 | static Client *getclient(HWND hwnd); 132 | LPSTR getclientclassname(HWND hwnd); 133 | LPSTR getclienttitle(HWND hwnd); 134 | HWND getroot(HWND hwnd); 135 | static void grabkeys(HWND hwnd); 136 | static void killclient(const Arg *arg); 137 | static Client *manage(HWND hwnd); 138 | static void monocle(void); 139 | static void movetonext(const Arg *arg); 140 | static void movetoprev(const Arg *arg); 141 | static Client *nextchild(Client *p, Client *c); 142 | static Client *nexttiled(Client *c); 143 | static void quit(const Arg *arg); 144 | static void resize(Client *c, int x, int y, int w, int h); 145 | static void restack(void); 146 | static BOOL CALLBACK scan(HWND hwnd, LPARAM lParam); 147 | static void setborder(Client *c, bool border); 148 | static void setvisibility(HWND hwnd, bool visibility); 149 | static void setlayout(const Arg *arg); 150 | static void setmfact(const Arg *arg); 151 | static void setmcount(const Arg *arg); 152 | static void setnmcolumn(const Arg *arg); 153 | static void setup(HINSTANCE hInstance); 154 | static void setupbar(HINSTANCE hInstance); 155 | static void showclientclassname(const Arg *arg); 156 | static void showhide(Client *c); 157 | static void spawn(const Arg *arg); 158 | static void tag(const Arg *arg); 159 | static int textnw(const char *text, unsigned int len); 160 | static void tile(void); 161 | static void htile(void); 162 | static void togglebar(const Arg *arg); 163 | static void toggleborder(const Arg *arg); 164 | static void toggleclock(const Arg *arg); 165 | static void toggleexplorer(const Arg *arg); 166 | static void togglefloating(const Arg *arg); 167 | static void toggletag(const Arg *arg); 168 | static void toggleview(const Arg *arg); 169 | static void unmanage(Client *c); 170 | static void updatebar(void); 171 | static void updategeom(void); 172 | static void view(const Arg *arg); 173 | static void zoom(const Arg *arg); 174 | 175 | /* Shell hook stuff */ 176 | 177 | typedef BOOL (*RegisterShellHookWindowProc) (HWND); 178 | RegisterShellHookWindowProc RegisterShellHookWindow; 179 | 180 | /* XXX: should be in a system header, no? */ 181 | typedef struct { 182 | HWND hwnd; 183 | RECT rc; 184 | } SHELLHOOKINFO, *LPSHELLHOOKINFO; 185 | 186 | /* variables */ 187 | static HWND dwmhwnd, barhwnd; 188 | static char stext[256]; 189 | static int sx, sy, sw, sh; /* X display screen geometry x, y, width, height */ 190 | static int by, bh, blw; /* bar geometry y, height and layout symbol width */ 191 | static int wx, wy, ww, wh; /* window area geometry x, y, width, height, bar excluded */ 192 | static unsigned int seltags, sellt; 193 | 194 | static Client *clients = NULL; 195 | static Client *sel = NULL; 196 | static Client *stack = NULL; 197 | static Layout *lt[] = { NULL, NULL }; 198 | static UINT shellhookid; /* Window Message id */ 199 | 200 | /* configuration, allows nested code to access above variables */ 201 | #include "config.h" 202 | 203 | /* compile-time check if all tags fit into an unsigned int bit array. */ 204 | struct NumTags { char limitexceeded[sizeof(unsigned int) * 8 < LENGTH(tags) ? -1 : 1]; }; 205 | 206 | /* elements of the window whose color should be set to the values in the array below */ 207 | static int colorwinelements[] = { COLOR_ACTIVEBORDER, COLOR_INACTIVEBORDER }; 208 | static COLORREF colors[2][LENGTH(colorwinelements)] = { 209 | { 0, 0 }, /* used to save the values before dwm started */ 210 | { selbordercolor, normbordercolor }, 211 | }; 212 | 213 | 214 | /* function implementations */ 215 | void 216 | applyrules(Client *c) { 217 | unsigned int i; 218 | Rule *r; 219 | 220 | /* rule matching */ 221 | for(i = 0; i < LENGTH(rules); i++) { 222 | r = &rules[i]; 223 | if((!r->title || strstr(getclienttitle(c->hwnd), r->title)) 224 | && (!r->class || strstr(getclientclassname(c->hwnd), r->class))) { 225 | c->isfloating = r->isfloating; 226 | c->tags |= r->tags & TAGMASK ? r->tags & TAGMASK : tagset[seltags]; 227 | } 228 | } 229 | if(!c->tags) 230 | c->tags = tagset[seltags]; 231 | } 232 | 233 | void 234 | arrange(void) { 235 | showhide(stack); 236 | focus(NULL); 237 | if(lt[sellt]->arrange) 238 | lt[sellt]->arrange(); 239 | restack(); 240 | } 241 | 242 | void 243 | attach(Client *c) { 244 | c->next = clients; 245 | clients = c; 246 | } 247 | 248 | void 249 | attachstack(Client *c) { 250 | c->snext = stack; 251 | stack = c; 252 | } 253 | 254 | bool 255 | istoolwindowof(Client *p, Client *c) { 256 | debug(" istoolwindowof: %s\n", getclienttitle(p->hwnd)); 257 | debug(" floating: %d\n", c->isfloating); 258 | debug(" root: %d == %d\n", p->root, c->root); 259 | debug(" threadid: %d == %d\n", p->threadid, c->threadid); 260 | return c->isfloating && (p->root == c->root || p->threadid == c->threadid); 261 | } 262 | 263 | void 264 | buttonpress(unsigned int button, POINTS *point) { 265 | unsigned int i, x, click; 266 | Arg arg = {0}; 267 | 268 | /* XXX: hack */ 269 | dc.hdc = GetWindowDC(barhwnd); 270 | 271 | i = x = 0; 272 | 273 | do { x += TEXTW(tags[i]); } while(point->x >= x && ++i < LENGTH(tags)); 274 | if(i < LENGTH(tags)) { 275 | click = ClkTagBar; 276 | arg.ui = 1 << i; 277 | } 278 | else if(point->x < x + blw) 279 | click = ClkLtSymbol; 280 | else if(point->x > wx + ww - TEXTW(stext)) 281 | click = ClkStatusText; 282 | else 283 | click = ClkWinTitle; 284 | 285 | if (GetKeyState(VK_SHIFT) < 0) 286 | return; 287 | 288 | for(i = 0; i < LENGTH(buttons); i++) { 289 | if(click == buttons[i].click && buttons[i].func && buttons[i].button == button 290 | && (!buttons[i].key || GetKeyState(buttons[i].key) < 0)) { 291 | buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); 292 | break; 293 | } 294 | } 295 | } 296 | 297 | void 298 | cleanup() { 299 | int i; 300 | Arg a = {.ui = ~0}; 301 | Layout foo = { "", NULL }; 302 | 303 | for(i = 0; i < LENGTH(keys); i++) { 304 | UnregisterHotKey(dwmhwnd, i); 305 | } 306 | 307 | DeregisterShellHookWindow(dwmhwnd); 308 | 309 | view(&a); 310 | lt[sellt] = &foo; 311 | while(stack) 312 | unmanage(stack); 313 | 314 | SetSysColors(LENGTH(colorwinelements), colorwinelements, colors[0]); 315 | 316 | DestroyWindow(dwmhwnd); 317 | } 318 | 319 | void 320 | clearurgent(Client *c) { 321 | c->isurgent = false; 322 | } 323 | 324 | void 325 | detach(Client *c) { 326 | Client **tc; 327 | 328 | for(tc = &clients; *tc && *tc != c; tc = &(*tc)->next); 329 | *tc = c->next; 330 | } 331 | 332 | void 333 | detachstack(Client *c) { 334 | Client **tc; 335 | 336 | for(tc = &stack; *tc && *tc != c; tc = &(*tc)->snext); 337 | *tc = c->snext; 338 | } 339 | 340 | void 341 | die(const char *errstr, ...) { 342 | va_list ap; 343 | 344 | va_start(ap, errstr); 345 | vfprintf(stderr, errstr, ap); 346 | va_end(ap); 347 | cleanup(); 348 | exit(EXIT_FAILURE); 349 | } 350 | 351 | void 352 | drawbar(void) { 353 | dc.hdc = GetWindowDC(barhwnd); 354 | 355 | dc.h = bh; 356 | 357 | int x; 358 | unsigned int i, occ = 0, urg = 0; 359 | unsigned long *col; 360 | Client *c; 361 | time_t timer; 362 | struct tm *date; 363 | char timestr[256]; 364 | 365 | for(c = clients; c; c = c->next) { 366 | occ |= c->tags; 367 | if(c->isurgent) 368 | urg |= c->tags; 369 | } 370 | 371 | dc.x = 0; 372 | for(i = 0; i < LENGTH(tags); i++) { 373 | dc.w = TEXTW(tags[i]); 374 | col = tagset[seltags] & 1 << i ? dc.sel : dc.norm; 375 | drawtext(tags[i], col, urg & 1 << i); 376 | drawsquare(sel && sel->tags & 1 << i, occ & 1 << i, urg & 1 << i, col); 377 | dc.x += dc.w; 378 | } 379 | if(blw > 0) { 380 | dc.w = blw; 381 | drawtext(lt[sellt]->symbol, dc.norm, false); 382 | x = dc.x + dc.w; 383 | } 384 | else 385 | x = dc.x; 386 | dc.w = TEXTW(stext); 387 | dc.x = ww - dc.w; 388 | if(dc.x < x) { 389 | dc.x = x; 390 | dc.w = ww - x; 391 | } 392 | drawtext(stext, dc.norm, false); 393 | 394 | if(showclock) { 395 | /* Draw Date Time */ 396 | timer = time(NULL); 397 | date = localtime(&timer); 398 | strftime(timestr, 255, clockfmt, date); 399 | dc.w = TEXTW(timestr); 400 | dc.x = ww - dc.w; 401 | drawtext(timestr, dc.norm, false); 402 | } 403 | 404 | if((dc.w = dc.x - x) > bh) { 405 | dc.x = x; 406 | if(sel) { 407 | drawtext(getclienttitle(sel->hwnd), dc.sel, false); 408 | drawsquare(sel->isfixed, sel->isfloating, false, dc.sel); 409 | } 410 | else 411 | drawtext(NULL, dc.norm, false); 412 | } 413 | 414 | ReleaseDC(barhwnd, dc.hdc); 415 | } 416 | 417 | void 418 | drawsquare(bool filled, bool empty, bool invert, COLORREF col[ColLast]) { 419 | static int size = 5; 420 | RECT r = { .left = dc.x + 1, .top = dc.y + 1, .right = dc.x + size, .bottom = dc.y + size }; 421 | 422 | HBRUSH brush = CreateSolidBrush(col[invert ? ColBG : ColFG]); 423 | SelectObject(dc.hdc, brush); 424 | 425 | if(filled) { 426 | FillRect(dc.hdc, &r, brush); 427 | } else if(empty) { 428 | FillRect(dc.hdc, &r, brush); 429 | } 430 | DeleteObject(brush); 431 | } 432 | 433 | void 434 | drawtext(const char *text, COLORREF col[ColLast], bool invert) { 435 | RECT r = { .left = dc.x, .top = dc.y, .right = dc.x + dc.w, .bottom = dc.y + dc.h }; 436 | 437 | HPEN pen = CreatePen(PS_SOLID, borderpx, selbordercolor); 438 | HBRUSH brush = CreateSolidBrush(col[invert ? ColFG : ColBG]); 439 | SelectObject(dc.hdc, pen); 440 | SelectObject(dc.hdc, brush); 441 | FillRect(dc.hdc, &r, brush); 442 | 443 | DeleteObject(brush); 444 | DeleteObject(pen); 445 | 446 | SetBkMode(dc.hdc, TRANSPARENT); 447 | SetTextColor(dc.hdc, col[invert ? ColBG : ColFG]); 448 | 449 | HFONT font = (HFONT)GetStockObject(SYSTEM_FONT); 450 | SelectObject(dc.hdc, font); 451 | 452 | DrawText(dc.hdc, text, -1, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE); 453 | } 454 | 455 | void 456 | eprint(const char *errstr, ...) { 457 | va_list ap; 458 | 459 | va_start(ap, errstr); 460 | vfprintf(stderr, errstr, ap); 461 | fflush(stderr); 462 | va_end(ap); 463 | } 464 | 465 | void 466 | setselected(Client *c) { 467 | if(!c || !ISVISIBLE(c)) 468 | for(c = stack; c && !ISVISIBLE(c); c = c->snext); 469 | if(sel && sel != c) 470 | drawborder(sel, normbordercolor); 471 | if(c) { 472 | if(c->isurgent) 473 | clearurgent(c); 474 | detachstack(c); 475 | attachstack(c); 476 | drawborder(c, selbordercolor); 477 | } 478 | sel = c; 479 | drawbar(); 480 | } 481 | 482 | void 483 | focus(Client *c) { 484 | setselected(c); 485 | if (sel) 486 | SetForegroundWindow(sel->hwnd); 487 | } 488 | 489 | void 490 | focusstack(const Arg *arg) { 491 | Client *c = NULL, *i; 492 | 493 | if(!sel) 494 | return; 495 | if (arg->i > 0) { 496 | for(c = sel->next; c && !ISFOCUSABLE(c); c = c->next); 497 | if(!c) 498 | for(c = clients; c && !ISFOCUSABLE(c); c = c->next); 499 | } 500 | else { 501 | for(i = clients; i != sel; i = i->next) 502 | if(ISFOCUSABLE(i)) 503 | c = i; 504 | if(!c) 505 | for(; i; i = i->next) 506 | if(ISFOCUSABLE(i)) 507 | c = i; 508 | } 509 | if(c) { 510 | focus(c); 511 | restack(); 512 | } 513 | } 514 | 515 | Client * 516 | managechildwindows(Client *p) { 517 | Client *c, *t; 518 | EnumChildWindows(p->hwnd, scan, 0); 519 | /* remove all child windows which were not part 520 | * of the enumeration above. 521 | */ 522 | for(c = clients; c; ) { 523 | if (c->parent == p->hwnd) { 524 | /* XXX: ismanageable isn't that reliable or some 525 | * windows change over time which means they 526 | * were once reported as manageable but not 527 | * this time so we also check if they are 528 | * currently visible and if that's the case 529 | * we keep them in our client list. 530 | */ 531 | if (!c->isalive && !IsWindowVisible(c->hwnd)) { 532 | t = c->next; 533 | unmanage(c); 534 | c = t; 535 | continue; 536 | } 537 | /* reset flag for next check */ 538 | c->isalive = false; 539 | } 540 | c = c->next; 541 | } 542 | 543 | return nextchild(p, clients); 544 | } 545 | 546 | Client * 547 | getclient(HWND hwnd) { 548 | Client *c; 549 | 550 | for(c = clients; c; c = c->next) 551 | if (c->hwnd == hwnd) 552 | return c; 553 | return NULL; 554 | } 555 | 556 | LPSTR 557 | getclientclassname(HWND hwnd) { 558 | static TCHAR buf[128]; 559 | GetClassName(hwnd, buf, sizeof buf); 560 | return buf; 561 | } 562 | 563 | LPSTR 564 | getclienttitle(HWND hwnd) { 565 | static TCHAR buf[128]; 566 | GetWindowText(hwnd, buf, sizeof buf); 567 | return buf; 568 | } 569 | 570 | HWND 571 | getroot(HWND hwnd){ 572 | HWND parent, deskwnd = GetDesktopWindow(); 573 | 574 | while ((parent = GetWindow(hwnd, GW_OWNER)) != NULL && deskwnd != parent) 575 | hwnd = parent; 576 | 577 | return hwnd; 578 | } 579 | 580 | void 581 | grabkeys(HWND hwnd) { 582 | int i; 583 | for (i = 0; i < LENGTH(keys); i++) { 584 | RegisterHotKey(hwnd, i, keys[i].mod, keys[i].key); 585 | } 586 | } 587 | 588 | bool 589 | ismanageable(HWND hwnd){ 590 | if (getclient(hwnd)) 591 | return true; 592 | 593 | HWND parent = GetParent(hwnd); 594 | HWND owner = GetWindow(hwnd, GW_OWNER); 595 | int style = GetWindowLong(hwnd, GWL_STYLE); 596 | int exstyle = GetWindowLong(hwnd, GWL_EXSTYLE); 597 | bool pok = (parent != 0 && ismanageable(parent)); 598 | bool istool = exstyle & WS_EX_TOOLWINDOW; 599 | bool isapp = exstyle & WS_EX_APPWINDOW; 600 | 601 | if (pok && !getclient(parent)) 602 | manage(parent); 603 | 604 | debug("ismanageable: %s\n", getclienttitle(hwnd)); 605 | debug(" hwnd: %d\n", hwnd); 606 | debug(" window: %d\n", IsWindow(hwnd)); 607 | debug(" visible: %d\n", IsWindowVisible(hwnd)); 608 | debug(" parent: %d\n", parent); 609 | debug("parentok: %d\n", pok); 610 | debug(" owner: %d\n", owner); 611 | debug(" toolwin: %d\n", istool); 612 | debug(" appwin: %d\n", isapp); 613 | 614 | /* XXX: should we do this? */ 615 | if (GetWindowTextLength(hwnd) == 0) { 616 | debug(" title: NULL\n"); 617 | debug(" manage: false\n"); 618 | return false; 619 | } 620 | 621 | if (style & WS_DISABLED) { 622 | debug("disabled: true\n"); 623 | debug(" manage: false\n"); 624 | return false; 625 | } 626 | 627 | /* 628 | * WS_EX_APPWINDOW 629 | * Forces a top-level window onto the taskbar when 630 | * the window is visible. 631 | * 632 | * WS_EX_TOOLWINDOW 633 | * Creates a tool window; that is, a window intended 634 | * to be used as a floating toolbar. A tool window 635 | * has a title bar that is shorter than a normal 636 | * title bar, and the window title is drawn using 637 | * a smaller font. A tool window does not appear in 638 | * the taskbar or in the dialog that appears when 639 | * the user presses ALT+TAB. If a tool window has 640 | * a system menu, its icon is not displayed on the 641 | * title bar. However, you can display the system 642 | * menu by right-clicking or by typing ALT+SPACE. 643 | */ 644 | 645 | if ((parent == 0 && IsWindowVisible(hwnd)) || pok) { 646 | if ((!istool && parent == 0) || (istool && pok)) { 647 | debug(" manage: true\n"); 648 | return true; 649 | } 650 | if (isapp && parent != 0) { 651 | debug(" manage: true\n"); 652 | return true; 653 | } 654 | } 655 | debug(" manage: false\n"); 656 | return false; 657 | } 658 | 659 | void 660 | killclient(const Arg *arg) { 661 | if(!sel) 662 | return; 663 | PostMessage(sel->hwnd, WM_CLOSE, 0, 0); 664 | } 665 | 666 | Client* 667 | manage(HWND hwnd) { 668 | static Client cz; 669 | Client *c = getclient(hwnd); 670 | 671 | if (c) 672 | return c; 673 | 674 | debug(" manage %s\n", getclienttitle(hwnd)); 675 | 676 | WINDOWINFO wi = { 677 | .cbSize = sizeof(WINDOWINFO), 678 | }; 679 | 680 | if (!GetWindowInfo(hwnd, &wi)) 681 | return NULL; 682 | 683 | if(!(c = malloc(sizeof(Client)))) 684 | die("fatal: could not malloc() %u bytes\n", sizeof(Client)); 685 | 686 | *c = cz; 687 | c->hwnd = hwnd; 688 | c->threadid = GetWindowThreadProcessId(hwnd, NULL); 689 | c->parent = GetParent(hwnd); 690 | c->root = getroot(hwnd); 691 | c->isalive = true; 692 | 693 | static WINDOWPLACEMENT wp = { 694 | .length = sizeof(WINDOWPLACEMENT), 695 | .showCmd = SW_RESTORE, 696 | }; 697 | 698 | if (IsWindowVisible(c->hwnd)) 699 | SetWindowPlacement(hwnd, &wp); 700 | 701 | /* maybe we could also filter based on 702 | * WS_MINIMIZEBOX and WS_MAXIMIZEBOX 703 | */ 704 | c->isfloating = (wi.dwStyle & WS_POPUP) || 705 | (!(wi.dwStyle & WS_MINIMIZEBOX) && !(wi.dwStyle & WS_MAXIMIZEBOX)); 706 | 707 | debug(" window style: %d\n", wi.dwStyle); 708 | debug(" minimize: %d\n", wi.dwStyle & WS_MINIMIZEBOX); 709 | debug(" maximize: %d\n", wi.dwStyle & WS_MAXIMIZEBOX); 710 | debug(" popup: %d\n", wi.dwStyle & WS_POPUP); 711 | debug(" isfloating: %d\n", c->isfloating); 712 | 713 | applyrules(c); 714 | 715 | if (!c->isfloating) 716 | setborder(c, false); 717 | 718 | if (c->isfloating && IsWindowVisible(hwnd)) { 719 | 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); 720 | resize(c, wi.rcWindow.left, wi.rcWindow.top, wi.rcWindow.right - wi.rcWindow.left, wi.rcWindow.bottom - wi.rcWindow.top); 721 | } 722 | 723 | attach(c); 724 | attachstack(c); 725 | return c; 726 | } 727 | 728 | void 729 | monocle(void) { 730 | Client *c; 731 | 732 | for(c = nexttiled(clients); c; c = nexttiled(c->next)) { 733 | resize(c, wx, wy, ww - 2 * c->bw, wh - 2 * c->bw); 734 | } 735 | } 736 | 737 | void 738 | movetonext(const Arg *arg) { 739 | Client *p, *n; 740 | 741 | if(!(p = clients)) 742 | return; 743 | for(; p && (p->next != sel); p = p->next); 744 | 745 | n = sel->next; 746 | 747 | if(!n) { 748 | /* sel is the last one */ 749 | zoom(NULL); 750 | return; 751 | } 752 | 753 | sel->next = n->next; 754 | n->next = sel; 755 | 756 | if(!p) { 757 | /* sel is the first one */ 758 | clients = n; 759 | } else { 760 | p->next = n; 761 | } 762 | 763 | arrange(); 764 | } 765 | 766 | void 767 | movetoprev(const Arg *arg) { 768 | Client *pp, *p; 769 | 770 | if(!(pp = clients) | !(p = pp->next)) 771 | return; 772 | 773 | if(sel == pp) { 774 | /* 775 | * sel is the first one; 776 | * there're at least 2 windows 777 | */ 778 | Client *l; 779 | for(l = p; l->next; l = l->next); 780 | 781 | l->next = sel; 782 | sel->next = NULL; 783 | clients = p; 784 | 785 | } else if(sel == p) { 786 | /* sel is the second one */ 787 | pp->next = sel->next; /* even if it equals NULL */ 788 | sel->next = pp; 789 | clients = sel; 790 | 791 | } else { 792 | for(; p && (p->next != sel); 793 | pp = pp->next, p = p->next); 794 | 795 | p->next = sel->next; 796 | sel->next = p; 797 | pp->next = sel; 798 | } 799 | 800 | arrange(); 801 | } 802 | 803 | Client * 804 | nextchild(Client *p, Client *c) { 805 | for(; c && c->parent != p->hwnd; c = c->next); 806 | return c; 807 | } 808 | 809 | Client * 810 | nexttiled(Client *c) { 811 | for(; c && (c->isfloating || IsIconic(c->hwnd) || !ISVISIBLE(c) || !IsWindowVisible(c->hwnd)); c = c->next); 812 | return c; 813 | } 814 | 815 | void 816 | quit(const Arg *arg) { 817 | PostMessage(dwmhwnd, WM_CLOSE, 0, 0); 818 | } 819 | 820 | void 821 | resize(Client *c, int x, int y, int w, int h) { 822 | if(w <= 0 && h <= 0) { 823 | setvisibility(c->hwnd, false); 824 | return; 825 | } 826 | if(x > sx + sw) 827 | x = sw - WIDTH(c); 828 | if(y > sy + sh) 829 | y = sh - HEIGHT(c); 830 | if(x + w + 2 * c->bw < sx) 831 | x = sx; 832 | if(y + h + 2 * c->bw < sy) 833 | y = sy; 834 | if(h < bh) 835 | h = bh; 836 | if(w < bh) 837 | w = bh; 838 | if(c->x != x || c->y != y || c->w != w || c->h != h) { 839 | c->x = x; 840 | c->y = y; 841 | c->w = w; 842 | c->h = h; 843 | debug(" resize %d: %s: x: %d y: %d w: %d h: %d\n", c->hwnd, getclienttitle(c->hwnd), x, y, w, h); 844 | SetWindowPos(c->hwnd, HWND_TOP, c->x, c->y, c->w, c->h, SWP_NOACTIVATE); 845 | } 846 | } 847 | 848 | void 849 | restack(void) { 850 | 851 | } 852 | 853 | LRESULT CALLBACK barhandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 854 | { 855 | switch (msg) { 856 | case WM_CREATE: 857 | updatebar(); 858 | break; 859 | case WM_PAINT: { 860 | PAINTSTRUCT ps; 861 | BeginPaint(hwnd, &ps); 862 | drawbar(); 863 | EndPaint(hwnd, &ps); 864 | break; 865 | } 866 | case WM_LBUTTONDOWN: 867 | case WM_RBUTTONDOWN: 868 | case WM_MBUTTONDOWN: 869 | buttonpress(msg, &MAKEPOINTS(lParam)); 870 | break; 871 | case WM_TIMER: 872 | drawbar(); 873 | default: 874 | return DefWindowProc(hwnd, msg, wParam, lParam); 875 | } 876 | 877 | return 0; 878 | } 879 | 880 | LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 881 | { 882 | switch (msg) { 883 | case WM_CREATE: 884 | break; 885 | case WM_CLOSE: 886 | cleanup(); 887 | break; 888 | case WM_DESTROY: 889 | PostQuitMessage(0); 890 | break; 891 | case WM_HOTKEY: 892 | if (wParam > 0 && wParam < LENGTH(keys)) { 893 | keys[wParam].func(&(keys[wParam ].arg)); 894 | } 895 | break; 896 | default: 897 | if (msg == shellhookid) { /* Handle the shell hook message */ 898 | Client *c = getclient((HWND)lParam); 899 | switch (wParam & 0x7fff) { 900 | /* The first two events are also trigger if windows 901 | * are being hidden or shown because of a tag 902 | * switch, therefore we ignore them in this case. 903 | */ 904 | case HSHELL_WINDOWCREATED: 905 | debug("window created: %s\n", getclienttitle((HWND)lParam)); 906 | if (!c && ismanageable((HWND)lParam)) { 907 | c = manage((HWND)lParam); 908 | managechildwindows(c); 909 | arrange(); 910 | } 911 | break; 912 | case HSHELL_WINDOWDESTROYED: 913 | if (c) { 914 | debug(" window %s: %s\n", c->ignore ? "hidden" : "destroyed", getclienttitle(c->hwnd)); 915 | if (!c->ignore) 916 | unmanage(c); 917 | else 918 | c->ignore = false; 919 | } else { 920 | debug(" unmanaged window destroyed\n"); 921 | } 922 | break; 923 | case HSHELL_WINDOWACTIVATED: 924 | debug(" window activated: %s || %d\n", c ? getclienttitle(c->hwnd) : "unknown", (HWND)lParam); 925 | if (c) { 926 | Client *t = sel; 927 | managechildwindows(c); 928 | setselected(c); 929 | /* check if the previously selected 930 | * window got minimized 931 | */ 932 | if (t && (t->isminimized = IsIconic(t->hwnd))) { 933 | debug(" active window got minimized: %s\n", getclienttitle(t->hwnd)); 934 | arrange(); 935 | } 936 | /* the newly focused window was minimized */ 937 | if (sel->isminimized) { 938 | debug(" newly active window was minimized: %s\n", getclienttitle(sel->hwnd)); 939 | sel->isminimized = false; 940 | zoom(NULL); 941 | } 942 | } else { 943 | /* Some window don't seem to generate 944 | * HSHELL_WINDOWCREATED messages therefore 945 | * we check here whether we should manage 946 | * the window or not. 947 | */ 948 | if (ismanageable((HWND)lParam)) { 949 | c = manage((HWND)lParam); 950 | managechildwindows(c); 951 | setselected(c); 952 | arrange(); 953 | } 954 | } 955 | break; 956 | } 957 | } else 958 | return DefWindowProc(hwnd, msg, wParam, lParam); 959 | } 960 | 961 | return 0; 962 | } 963 | 964 | BOOL CALLBACK 965 | scan(HWND hwnd, LPARAM lParam) { 966 | Client *c = getclient(hwnd); 967 | if (c) 968 | c->isalive = true; 969 | else if (ismanageable(hwnd)) 970 | manage(hwnd); 971 | return TRUE; 972 | } 973 | 974 | void 975 | drawborder(Client *c, COLORREF color) { 976 | #if 0 977 | HDC hdc = GetWindowDC(c->hwnd); 978 | 979 | #if 0 980 | /* this would be another way, but it uses standard sytem colors */ 981 | RECT area = { .left = 0, .top = 0, .right = c->w, .bottom = c->h }; 982 | DrawEdge(hdc, &area, BDR_RAISEDOUTER | BDR_SUNKENINNER, BF_RECT); 983 | #else 984 | HPEN pen = CreatePen(PS_SOLID, borderpx, color); 985 | SelectObject(hdc, pen); 986 | MoveToEx(hdc, 0, 0, NULL); 987 | LineTo(hdc, c->w, 0); 988 | LineTo(hdc, c->w, c->h); 989 | LineTo(hdc, 0, c->h); 990 | LineTo(hdc, 0, 0); 991 | DeleteObject(pen); 992 | #endif 993 | 994 | ReleaseDC(c->hwnd, hdc); 995 | #endif 996 | } 997 | 998 | void 999 | setborder(Client *c, bool border) { 1000 | if (border) { 1001 | SetWindowLong(c->hwnd, GWL_STYLE, (GetWindowLong(c->hwnd, GWL_STYLE) | (WS_CAPTION | WS_SIZEBOX))); 1002 | } else { 1003 | /* XXX: ideally i would like to use the standard window border facilities and just modify the 1004 | * color with SetSysColor but this only seems to work if we leave WS_SIZEBOX enabled which 1005 | * is not optimal. 1006 | */ 1007 | SetWindowLong(c->hwnd, GWL_STYLE, (GetWindowLong(c->hwnd, GWL_STYLE) & ~(WS_CAPTION | WS_SIZEBOX)) | WS_BORDER | WS_THICKFRAME); 1008 | SetWindowLong(c->hwnd, GWL_EXSTYLE, (GetWindowLong(c->hwnd, GWL_EXSTYLE) & ~(WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE))); 1009 | } 1010 | SetWindowPos(c->hwnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER ); 1011 | c->border = border; 1012 | } 1013 | 1014 | void 1015 | setvisibility(HWND hwnd, bool visibility) { 1016 | SetWindowPos(hwnd, 0, 0, 0, 0, 0, (visibility ? SWP_SHOWWINDOW : SWP_HIDEWINDOW) | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER); 1017 | } 1018 | 1019 | void 1020 | setlayout(const Arg *arg) { 1021 | if(!arg || !arg->v || arg->v != lt[sellt]) 1022 | sellt ^= 1; 1023 | if(arg && arg->v) 1024 | lt[sellt] = (Layout *)arg->v; 1025 | if(sel) 1026 | arrange(); 1027 | else 1028 | drawbar(); 1029 | } 1030 | 1031 | /* arg > 1.0 will set mfact absolutly */ 1032 | void 1033 | setmfact(const Arg *arg) { 1034 | float f; 1035 | 1036 | if(!arg || !lt[sellt]->arrange) 1037 | return; 1038 | f = arg->f < 1.0 ? arg->f + mfact : arg->f - 1.0; 1039 | if(f < 0.1 || f > 0.9) 1040 | return; 1041 | mfact = f; 1042 | arrange(); 1043 | } 1044 | 1045 | void 1046 | setmcount(const Arg *arg) { 1047 | int i; 1048 | 1049 | if(!arg || !lt[sellt]->arrange) 1050 | return; 1051 | i = mcount + arg->i; 1052 | if (i < 1) 1053 | return; 1054 | mcount = i; 1055 | arrange(); 1056 | } 1057 | 1058 | void 1059 | setnmcolumn(const Arg *arg) { 1060 | int i; 1061 | 1062 | if(!arg || !lt[sellt]->arrange) 1063 | return; 1064 | i = nmcolumn + arg->i; 1065 | if (i < 1) 1066 | return; 1067 | nmcolumn = i; 1068 | arrange(); 1069 | } 1070 | 1071 | void 1072 | setup(HINSTANCE hInstance) { 1073 | 1074 | unsigned int i; 1075 | 1076 | lt[0] = &layouts[0]; 1077 | lt[1] = &layouts[1 % LENGTH(layouts)]; 1078 | 1079 | /* init appearance */ 1080 | 1081 | dc.norm[ColBorder] = normbordercolor; 1082 | dc.norm[ColBG] = normbgcolor; 1083 | dc.norm[ColFG] = normfgcolor; 1084 | dc.sel[ColBorder] = selbordercolor; 1085 | dc.sel[ColBG] = selbgcolor; 1086 | dc.sel[ColFG] = selfgcolor; 1087 | 1088 | /* save colors so we can restore them in cleanup */ 1089 | for (i = 0; i < LENGTH(colorwinelements); i++) 1090 | colors[0][i] = GetSysColor(colorwinelements[i]); 1091 | 1092 | SetSysColors(LENGTH(colorwinelements), colorwinelements, colors[1]); 1093 | 1094 | //SystemParametersInfo(SPI_SETBORDER, 1 /* width */, 0, 0); 1095 | 1096 | updategeom(); 1097 | 1098 | WNDCLASSEX winClass; 1099 | 1100 | winClass.cbSize = sizeof(WNDCLASSEX); 1101 | winClass.style = 0; 1102 | winClass.lpfnWndProc = WndProc; 1103 | winClass.cbClsExtra = 0; 1104 | winClass.cbWndExtra = 0; 1105 | winClass.hInstance = hInstance; 1106 | winClass.hIcon = NULL; 1107 | winClass.hIconSm = NULL; 1108 | winClass.hCursor = NULL; 1109 | winClass.hbrBackground = NULL; 1110 | winClass.lpszMenuName = NULL; 1111 | winClass.lpszClassName = NAME; 1112 | 1113 | if (!RegisterClassEx(&winClass)) 1114 | die("Error registering window class"); 1115 | 1116 | dwmhwnd = CreateWindowEx(0, NAME, NAME, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, hInstance, NULL); 1117 | 1118 | if (!dwmhwnd) 1119 | die("Error creating window"); 1120 | 1121 | grabkeys(dwmhwnd); 1122 | 1123 | EnumWindows(scan, 0); 1124 | 1125 | setupbar(hInstance); 1126 | 1127 | arrange(); 1128 | 1129 | /* Get function pointer for RegisterShellHookWindow */ 1130 | RegisterShellHookWindow = (RegisterShellHookWindowProc)GetProcAddress(GetModuleHandle("USER32.DLL"), "RegisterShellHookWindow"); 1131 | if (!RegisterShellHookWindow) 1132 | die("Could not find RegisterShellHookWindow"); 1133 | RegisterShellHookWindow(dwmhwnd); 1134 | /* Grab a dynamic id for the SHELLHOOK message to be used later */ 1135 | shellhookid = RegisterWindowMessage("SHELLHOOK"); 1136 | } 1137 | 1138 | void 1139 | setupbar(HINSTANCE hInstance) { 1140 | 1141 | unsigned int i, w = 0; 1142 | 1143 | WNDCLASS winClass; 1144 | memset(&winClass, 0, sizeof winClass); 1145 | 1146 | winClass.style = 0; 1147 | winClass.lpfnWndProc = barhandler; 1148 | winClass.cbClsExtra = 0; 1149 | winClass.cbWndExtra = 0; 1150 | winClass.hInstance = hInstance; 1151 | winClass.hIcon = NULL; 1152 | winClass.hCursor = LoadCursor(NULL, IDC_ARROW); 1153 | winClass.hbrBackground = NULL; 1154 | winClass.lpszMenuName = NULL; 1155 | winClass.lpszClassName = "dwm-bar"; 1156 | 1157 | if (!RegisterClass(&winClass)) 1158 | die("Error registering window class"); 1159 | 1160 | barhwnd = CreateWindowEx( 1161 | WS_EX_TOOLWINDOW, 1162 | "dwm-bar", 1163 | NULL, /* window title */ 1164 | WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 1165 | 0, 0, 0, 0, 1166 | NULL, /* parent window */ 1167 | NULL, /* menu */ 1168 | hInstance, 1169 | NULL 1170 | ); 1171 | 1172 | /* calculate width of the largest layout symbol */ 1173 | dc.hdc = GetWindowDC(barhwnd); 1174 | HFONT font = (HFONT)GetStockObject(SYSTEM_FONT); 1175 | SelectObject(dc.hdc, font); 1176 | 1177 | for(blw = i = 0; LENGTH(layouts) > 1 && i < LENGTH(layouts); i++) { 1178 | w = TEXTW(layouts[i].symbol); 1179 | blw = MAX(blw, w); 1180 | } 1181 | 1182 | ReleaseDC(barhwnd, dc.hdc); 1183 | 1184 | PostMessage(barhwnd, WM_PAINT, 0, 0); 1185 | 1186 | SetTimer(barhwnd, 1, clock_intval, NULL); 1187 | 1188 | updatebar(); 1189 | } 1190 | 1191 | void 1192 | showclientclassname(const Arg *arg) { 1193 | MessageBox(NULL, getclientclassname(GetForegroundWindow()), "Window class", MB_OK); 1194 | } 1195 | 1196 | void 1197 | showhide(Client *c) { 1198 | if(!c) 1199 | return; 1200 | /* XXX: is the order of showing / hidding important? */ 1201 | if (!ISVISIBLE(c)) { 1202 | if (IsWindowVisible(c->hwnd)) { 1203 | c->ignore = true; 1204 | c->wasvisible = true; 1205 | setvisibility(c->hwnd, false); 1206 | } 1207 | } else { 1208 | if (c->wasvisible) { 1209 | setvisibility(c->hwnd, true); 1210 | } 1211 | } 1212 | showhide(c->snext); 1213 | } 1214 | 1215 | void 1216 | spawn(const Arg *arg) { 1217 | ShellExecute(NULL, NULL, ((char **)arg->v)[0], ((char **)arg->v)[1], NULL, SW_SHOWDEFAULT); 1218 | } 1219 | 1220 | void 1221 | tag(const Arg *arg) { 1222 | Client *c; 1223 | 1224 | if(sel && arg->ui & TAGMASK) { 1225 | sel->tags = arg->ui & TAGMASK; 1226 | debug("window tagged: %d %s\n", sel->hwnd, getclienttitle(sel->hwnd)); 1227 | for (c = managechildwindows(sel); c; c = nextchild(sel, c->next)) { 1228 | debug(" child window which is %s tagged: %s\n", c->isfloating ? "floating" : "normal", getclienttitle(c->hwnd)); 1229 | if (c->isfloating) 1230 | c->tags = arg->ui & TAGMASK; 1231 | } 1232 | debug("window tagged finished\n"); 1233 | arrange(); 1234 | } 1235 | } 1236 | 1237 | int 1238 | textnw(const char *text, unsigned int len) { 1239 | SIZE size; 1240 | GetTextExtentPoint(dc.hdc, text, len, &size); 1241 | if (size.cx > 0) 1242 | size.cx += textmargin; 1243 | return size.cx; 1244 | } 1245 | 1246 | 1247 | void 1248 | tile(void) { 1249 | 1250 | int x, y, h, w, mw, mn; 1251 | unsigned int i, j, n; 1252 | unsigned int nmc, nmr[nmcolumn]; 1253 | Client *c; 1254 | 1255 | for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next), n++); 1256 | if(n == 0) 1257 | return; 1258 | 1259 | /* masters */ 1260 | x = wx; 1261 | y = wy; 1262 | mw = (n <= mcount ? ww : mfact * ww); 1263 | mn = MIN(n, mcount); 1264 | h = wh / mn; 1265 | for(i = 0, c = nexttiled(clients); i < mn; c = nexttiled(c->next), i++) { 1266 | resize(c, x, y, mw - 2 * c->bw, /* remainder */ ((i + 1 == mn) 1267 | ? wy + wh - y - 2 * c->bw : h - 2 * c->bw)); 1268 | if(h != wh) 1269 | y = c->y + HEIGHT(c); 1270 | n--; 1271 | } 1272 | if(n == 0) 1273 | return; 1274 | 1275 | /* now c is the top window of tile stacks */ 1276 | 1277 | nmc = MIN(n, nmcolumn); 1278 | j = n / nmc; 1279 | for(i = 0; i < nmc; i++) 1280 | nmr[i] = j; 1281 | j = n % nmc; 1282 | for(i = 0; i < j; i++) 1283 | nmr[nmc - 1 - i]++; 1284 | 1285 | x = wx + mw; 1286 | w = (ww - mw) / nmc; 1287 | for(i = 0; i < nmc; i++) { 1288 | y = wy; 1289 | h = wh / nmr[i]; 1290 | if(h < bh) 1291 | h = wh; 1292 | 1293 | for(j = 0; j < nmr[i]; c = nexttiled(c->next), j++) { 1294 | resize(c, x, y, w - 2 * c->bw, /* remainder */ ((j + 1 == nmr[i]) 1295 | ? wy + wh - y - 2 * c->bw : h - 2 * c->bw)); 1296 | if(h != wh) 1297 | y = c->y + HEIGHT(c); 1298 | } 1299 | 1300 | x += w; 1301 | } 1302 | } 1303 | 1304 | 1305 | void 1306 | htile(void) { 1307 | 1308 | int x, y, h, w, mh, mn; 1309 | unsigned int i, j, n; 1310 | unsigned int nmc, nmr[nmcolumn]; 1311 | Client *c; 1312 | 1313 | for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next), n++); 1314 | if(n == 0) 1315 | return; 1316 | 1317 | /* masters */ 1318 | x = wx; 1319 | y = wy; 1320 | mh = (n <= mcount ? wh : mfact * wh); 1321 | mn = MIN(n, mcount); 1322 | w = ww / mn; 1323 | for(i = 0, c = nexttiled(clients); i < mn; c = nexttiled(c->next), i++) { 1324 | resize(c, x, y, /* remainder */ ((i + 1 == mn) 1325 | ? wx + ww - x - 2 * c->bw : w - 2 * c->bw), mh - 2 * c->bw); 1326 | if(w != ww) 1327 | x = c->x + WIDTH(c); 1328 | n--; 1329 | } 1330 | if(n == 0) 1331 | return; 1332 | 1333 | /* now c is the top window of tile stacks */ 1334 | 1335 | nmc = MIN(n, nmcolumn); 1336 | j = n / nmc; 1337 | for(i = 0; i < nmc; i++) 1338 | nmr[i] = j; 1339 | j = n % nmc; 1340 | for(i = 0; i < j; i++) 1341 | nmr[nmc - 1 - i]++; 1342 | 1343 | y = wy + mh; 1344 | h = (wh - mh) / nmc; 1345 | for(i = 0; i < nmc; i++) { 1346 | x = wx; 1347 | w = ww / nmr[i]; 1348 | if(w < bh) /* I don't undersand this */ 1349 | w = ww; 1350 | 1351 | for(j = 0; j < nmr[i]; c = nexttiled(c->next), j++) { 1352 | resize(c, x, y, /* remainder */ ((j + 1 == nmr[i]) 1353 | ? wx + ww - x - 2 * c->bw : w - 2 * c->bw), h - 2 * c->bw); 1354 | if(w != ww) 1355 | x = c->x + WIDTH(c); 1356 | } 1357 | 1358 | y += h; 1359 | } 1360 | } 1361 | 1362 | void 1363 | togglebar(const Arg *arg) { 1364 | showbar = !showbar; 1365 | updategeom(); 1366 | updatebar(); 1367 | arrange(); 1368 | } 1369 | 1370 | void 1371 | toggleborder(const Arg *arg) { 1372 | if (!sel) 1373 | return; 1374 | setborder(sel, !sel->border); 1375 | } 1376 | 1377 | void 1378 | toggleexplorer(const Arg *arg) { 1379 | HWND hwnd = FindWindow("Progman", "Program Manager"); 1380 | if (hwnd) 1381 | setvisibility(hwnd, !IsWindowVisible(hwnd)); 1382 | 1383 | hwnd = FindWindow("Shell_TrayWnd", NULL); 1384 | if (hwnd) 1385 | setvisibility(hwnd, !IsWindowVisible(hwnd)); 1386 | 1387 | 1388 | updategeom(); 1389 | updatebar(); 1390 | arrange(); 1391 | } 1392 | 1393 | void 1394 | toggleclock(const Arg *arg) { 1395 | showclock = !showclock; 1396 | updatebar(); 1397 | arrange(); 1398 | } 1399 | 1400 | void 1401 | togglefloating(const Arg *arg) { 1402 | if(!sel) 1403 | return; 1404 | sel->isfloating = !sel->isfloating || sel->isfixed; 1405 | setborder(sel, sel->isfloating); 1406 | if(sel->isfloating) 1407 | resize(sel, sel->x, sel->y, sel->w, sel->h); 1408 | arrange(); 1409 | } 1410 | 1411 | void 1412 | toggletag(const Arg *arg) { 1413 | unsigned int mask; 1414 | 1415 | if (!sel) 1416 | return; 1417 | 1418 | mask = sel->tags ^ (arg->ui & TAGMASK); 1419 | if(mask) { 1420 | sel->tags = mask; 1421 | arrange(); 1422 | } 1423 | } 1424 | 1425 | void 1426 | toggleview(const Arg *arg) { 1427 | unsigned int mask = tagset[seltags] ^ (arg->ui & TAGMASK); 1428 | 1429 | if(mask) { 1430 | tagset[seltags] = mask; 1431 | arrange(); 1432 | } 1433 | } 1434 | 1435 | void 1436 | unmanage(Client *c) { 1437 | debug(" unmanage %s\n", getclienttitle(c->hwnd)); 1438 | if (c->wasvisible) 1439 | setvisibility(c->hwnd, true); 1440 | if (!c->isfloating) 1441 | setborder(c, true); 1442 | detach(c); 1443 | detachstack(c); 1444 | if(sel == c) 1445 | focus(NULL); 1446 | free(c); 1447 | arrange(); 1448 | } 1449 | 1450 | void 1451 | updatebar(void) { 1452 | SetWindowPos(barhwnd, showbar ? HWND_TOPMOST : HWND_NOTOPMOST, 0, by, ww, bh, (showbar ? SWP_SHOWWINDOW : SWP_HIDEWINDOW) | SWP_NOACTIVATE | SWP_NOSENDCHANGING); 1453 | } 1454 | 1455 | void 1456 | updategeom(void) { 1457 | RECT wa; 1458 | HWND hwnd = FindWindow("Shell_TrayWnd", NULL); 1459 | /* check if the windows taskbar is visible and adjust 1460 | * the workspace accordingly. 1461 | */ 1462 | if (hwnd && IsWindowVisible(hwnd)) { 1463 | SystemParametersInfo(SPI_GETWORKAREA, 0, &wa, 0); 1464 | sx = wa.left; 1465 | sy = wa.top; 1466 | sw = wa.right - wa.left; 1467 | sh = wa.bottom - wa.top; 1468 | } else { 1469 | sx = GetSystemMetrics(SM_XVIRTUALSCREEN); 1470 | sy = GetSystemMetrics(SM_YVIRTUALSCREEN); 1471 | sw = GetSystemMetrics(SM_CXVIRTUALSCREEN); 1472 | sh = GetSystemMetrics(SM_CYVIRTUALSCREEN); 1473 | } 1474 | 1475 | bh = 20; /* XXX: fixed value */ 1476 | 1477 | /* window area geometry */ 1478 | wx = sx; 1479 | wy = showbar && topbar ? sy + bh : sy; 1480 | ww = sw; 1481 | wh = showbar ? sh - bh : sh; 1482 | /* bar position */ 1483 | by = showbar ? (topbar ? wy - bh : wy + wh) : -bh; 1484 | debug("updategeom: %d x %d\n", ww, wh); 1485 | } 1486 | 1487 | void 1488 | view(const Arg *arg) { 1489 | if((arg->ui & TAGMASK) == tagset[seltags]) 1490 | return; 1491 | seltags ^= 1; /* toggle sel tagset */ 1492 | if(arg->ui & TAGMASK) 1493 | tagset[seltags] = arg->ui & TAGMASK; 1494 | arrange(); 1495 | } 1496 | 1497 | void 1498 | zoom(const Arg *arg) { 1499 | Client *c = sel; 1500 | 1501 | if(!lt[sellt]->arrange || lt[sellt]->arrange == monocle || (sel && sel->isfloating)) 1502 | return; 1503 | if(c == nexttiled(clients)) 1504 | if(!c || !(c = nexttiled(c->next))) 1505 | return; 1506 | detach(c); 1507 | attach(c); 1508 | focus(c); 1509 | arrange(); 1510 | } 1511 | 1512 | int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { 1513 | MSG msg; 1514 | 1515 | setup(hInstance); 1516 | 1517 | while (GetMessage(&msg, NULL, 0, 0) > 0) { 1518 | TranslateMessage(&msg); 1519 | DispatchMessage(&msg); 1520 | } 1521 | 1522 | cleanup(); 1523 | 1524 | return msg.wParam; 1525 | } 1526 | --------------------------------------------------------------------------------