├── dwm.png ├── bread_dwm.png ├── util.h ├── util.c ├── transient.c ├── Makefile ├── config.mk ├── drw.h ├── LICENSE ├── README.md ├── dwm.1 ├── vanitygaps.c ├── drw.c ├── config.def.h ├── swallow └── dwm.c /dwm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BreadOnPenguins/dwm/HEAD/dwm.png -------------------------------------------------------------------------------- /bread_dwm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BreadOnPenguins/dwm/HEAD/bread_dwm.png -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | #define MAX(A, B) ((A) > (B) ? (A) : (B)) 4 | #define MIN(A, B) ((A) < (B) ? (A) : (B)) 5 | #define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) 6 | #define LENGTH(X) (sizeof (X) / sizeof (X)[0]) 7 | 8 | void die(const char *fmt, ...); 9 | void *ecalloc(size_t nmemb, size_t size); 10 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "util.h" 9 | 10 | void 11 | die(const char *fmt, ...) 12 | { 13 | va_list ap; 14 | int saved_errno; 15 | 16 | saved_errno = errno; 17 | 18 | va_start(ap, fmt); 19 | vfprintf(stderr, fmt, ap); 20 | va_end(ap); 21 | 22 | if (fmt[0] && fmt[strlen(fmt)-1] == ':') 23 | fprintf(stderr, " %s", strerror(saved_errno)); 24 | fputc('\n', stderr); 25 | 26 | exit(1); 27 | } 28 | 29 | void * 30 | ecalloc(size_t nmemb, size_t size) 31 | { 32 | void *p; 33 | 34 | if (!(p = calloc(nmemb, size))) 35 | die("calloc:"); 36 | return p; 37 | } 38 | -------------------------------------------------------------------------------- /transient.c: -------------------------------------------------------------------------------- 1 | /* cc transient.c -o transient -lX11 */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(void) { 9 | Display *d; 10 | Window r, f, t = None; 11 | XSizeHints h; 12 | XEvent e; 13 | 14 | d = XOpenDisplay(NULL); 15 | if (!d) 16 | exit(1); 17 | r = DefaultRootWindow(d); 18 | 19 | f = XCreateSimpleWindow(d, r, 100, 100, 400, 400, 0, 0, 0); 20 | h.min_width = h.max_width = h.min_height = h.max_height = 400; 21 | h.flags = PMinSize | PMaxSize; 22 | XSetWMNormalHints(d, f, &h); 23 | XStoreName(d, f, "floating"); 24 | XMapWindow(d, f); 25 | 26 | XSelectInput(d, f, ExposureMask); 27 | while (1) { 28 | XNextEvent(d, &e); 29 | 30 | if (t == None) { 31 | sleep(5); 32 | t = XCreateSimpleWindow(d, r, 50, 50, 100, 100, 0, 0, 0); 33 | XSetTransientForHint(d, t, f); 34 | XStoreName(d, t, "transient"); 35 | XMapWindow(d, t); 36 | XSelectInput(d, t, ExposureMask); 37 | } 38 | } 39 | 40 | XCloseDisplay(d); 41 | exit(0); 42 | } 43 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # dwm - dynamic window manager 2 | # See LICENSE file for copyright and license details. 3 | 4 | include config.mk 5 | 6 | SRC = drw.c dwm.c util.c 7 | OBJ = ${SRC:.c=.o} 8 | 9 | all: dwm 10 | 11 | .c.o: 12 | ${CC} -c ${CFLAGS} $< 13 | 14 | ${OBJ}: config.h config.mk 15 | 16 | config.h: 17 | cp config.def.h $@ 18 | 19 | dwm: ${OBJ} 20 | ${CC} -o $@ ${OBJ} ${LDFLAGS} 21 | 22 | clean: 23 | rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz 24 | 25 | dist: clean 26 | mkdir -p dwm-${VERSION} 27 | cp -R LICENSE Makefile README config.def.h config.mk\ 28 | dwm.1 drw.h util.h ${SRC} dwm.png transient.c dwm-${VERSION} 29 | tar -cf dwm-${VERSION}.tar dwm-${VERSION} 30 | gzip dwm-${VERSION}.tar 31 | rm -rf dwm-${VERSION} 32 | 33 | install: all 34 | mkdir -p ${DESTDIR}${PREFIX}/bin 35 | cp -f dwm ${DESTDIR}${PREFIX}/bin 36 | chmod 755 ${DESTDIR}${PREFIX}/bin/dwm 37 | mkdir -p ${DESTDIR}${MANPREFIX}/man1 38 | sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1 39 | chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1 40 | 41 | uninstall: 42 | rm -f ${DESTDIR}${PREFIX}/bin/dwm\ 43 | ${DESTDIR}${MANPREFIX}/man1/dwm.1 44 | 45 | .PHONY: all clean dist install uninstall 46 | -------------------------------------------------------------------------------- /config.mk: -------------------------------------------------------------------------------- 1 | # dwm version 2 | VERSION = 6.5 3 | 4 | # Customize below to fit your system 5 | 6 | # paths 7 | PREFIX = /usr/local 8 | MANPREFIX = ${PREFIX}/share/man 9 | 10 | OS := $(shell uname) 11 | ifeq ($(OS), FreeBSD) 12 | X11INC = /usr/local/include 13 | X11LIB = /usr/local/lib 14 | else 15 | X11INC = /usr/X11R6/include 16 | X11LIB = /usr/X11R6/lib 17 | endif 18 | 19 | # Xinerama, comment if you don't want it 20 | XINERAMALIBS = -lXinerama 21 | XINERAMAFLAGS = -DXINERAMA 22 | 23 | # freetype 24 | FREETYPELIBS = -lfontconfig -lXft 25 | ifeq ($(OS), FreeBSD) 26 | FREETYPEINC = ${X11INC}/freetype2 27 | else 28 | FREETYPEINC = /usr/include/freetype2 29 | endif 30 | # OpenBSD (uncomment) 31 | #FREETYPEINC = ${X11INC}/freetype2 32 | #MANPREFIX = ${PREFIX}/man 33 | #KVMLIB = -lkvm 34 | 35 | # includes and libs 36 | INCS = -I${X11INC} -I${FREETYPEINC} 37 | LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lX11-xcb -lxcb -lxcb-res ${KVMLIB} 38 | 39 | # flags 40 | CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} 41 | #CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} 42 | CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} 43 | LDFLAGS = ${LIBS} 44 | 45 | # Solaris 46 | #CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" 47 | #LDFLAGS = ${LIBS} 48 | 49 | # compiler and linker 50 | CC = cc 51 | -------------------------------------------------------------------------------- /drw.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | typedef struct { 4 | Cursor cursor; 5 | } Cur; 6 | 7 | typedef struct Fnt { 8 | Display *dpy; 9 | unsigned int h; 10 | XftFont *xfont; 11 | FcPattern *pattern; 12 | struct Fnt *next; 13 | } Fnt; 14 | 15 | enum { ColFg, ColBg, ColBorder }; /* Clr scheme index */ 16 | typedef XftColor Clr; 17 | 18 | typedef struct { 19 | unsigned int w, h; 20 | Display *dpy; 21 | int screen; 22 | Window root; 23 | Drawable drawable; 24 | GC gc; 25 | Clr *scheme; 26 | Fnt *fonts; 27 | } Drw; 28 | 29 | /* Drawable abstraction */ 30 | Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); 31 | void drw_resize(Drw *drw, unsigned int w, unsigned int h); 32 | void drw_free(Drw *drw); 33 | 34 | /* Fnt abstraction */ 35 | Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); 36 | void drw_fontset_free(Fnt* set); 37 | unsigned int drw_fontset_getwidth(Drw *drw, const char *text); 38 | unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n); 39 | void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); 40 | 41 | /* Colorscheme abstraction */ 42 | void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); 43 | Clr *drw_scm_create(Drw *drw, char *clrnames[], size_t clrcount); 44 | 45 | /* Cursor abstraction */ 46 | Cur *drw_cur_create(Drw *drw, int shape); 47 | void drw_cur_free(Drw *drw, Cur *cursor); 48 | 49 | /* Drawing context manipulation */ 50 | void drw_setfontset(Drw *drw, Fnt *set); 51 | void drw_setscheme(Drw *drw, Clr *scm); 52 | 53 | /* Drawing functions */ 54 | void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); 55 | int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); 56 | 57 | /* Map functions */ 58 | void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT/X Consortium License 2 | 3 | © 2006-2019 Anselm R Garbe 4 | © 2006-2009 Jukka Salmi 5 | © 2006-2007 Sander van Dijk 6 | © 2007-2011 Peter Hartlich 7 | © 2007-2009 Szabolcs Nagy 8 | © 2007-2009 Christof Musik 9 | © 2007-2009 Premysl Hruby 10 | © 2007-2008 Enno Gottox Boland 11 | © 2008 Martin Hurton 12 | © 2008 Neale Pickett 13 | © 2009 Mate Nagy 14 | © 2010-2016 Hiltjo Posthuma 15 | © 2010-2012 Connor Lane Smith 16 | © 2011 Christoph Lohmann <20h@r-36.net> 17 | © 2015-2016 Quentin Rameau 18 | © 2015-2016 Eric Pruitt 19 | © 2016-2017 Markus Teich 20 | © 2020-2022 Chris Down 21 | 22 | Permission is hereby granted, free of charge, to any person obtaining a 23 | copy of this software and associated documentation files (the "Software"), 24 | to deal in the Software without restriction, including without limitation 25 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 26 | and/or sell copies of the Software, and to permit persons to whom the 27 | Software is furnished to do so, subject to the following conditions: 28 | 29 | The above copyright notice and this permission notice shall be included in 30 | all copies or substantial portions of the Software. 31 | 32 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 33 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 34 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 35 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 36 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 37 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 38 | DEALINGS IN THE SOFTWARE. 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## dwm - dynamic window manager - bread's build 2 | ![my build](bread_dwm.png) 3 | comes with no guarantees or warranties (this means things may not work as expected, or at all) :^) 4 | 5 | note: I'd highly recommend setting up your own build! using mine or anyone else's is a starting point, but patching dwm manually not only allows for precise customization, but it will also allow you to learn how your WM works. 6 | a great way to test build changes without refreshing/restarting your running WM is with [Xephyr](https://wiki.archlinux.org/title/Xephyr), a nested X server that runs as an application. 7 | 8 | ## patches applied: 9 | some occasional modification here and there; 10 | * [bartoggle keybinds](https://dwm.suckless.org/patches/bartoggle/) 11 | * [bulkill](https://dwm.suckless.org/patches/bulkill/) 12 | * [colorbar](https://dwm.suckless.org/patches/colorbar/) 13 | * [fixmultimon](https://dwm.suckless.org/patches/fixmultimon/) 14 | * [focusfullscreen](https://dwm.suckless.org/patches/focusfullscreen/) 15 | * [focusmaster-return](https://dwm.suckless.org/patches/focusmaster/) 16 | * [focusmonmouse](https://dwm.suckless.org/patches/focusmonmouse/) 17 | * [hide vacant tags](https://dwm.suckless.org/patches/hide_vacant_tags/) 18 | * [preventfocusshift](https://dwm.suckless.org/patches/preventfocusshift/) 19 | * [restartsig](https://dwm.suckless.org/patches/restartsig/) 20 | * [spawntag](https://dwm.suckless.org/patches/spawntag/) 21 | * [stacker](https://dwm.suckless.org/patches/stacker/) 22 | * [statuscmd](https://dwm.suckless.org/patches/statuscmd/) 23 | * [sticky](https://dwm.suckless.org/patches/sticky/) 24 | * [swallow](https://dwm.suckless.org/patches/swallow/) 25 | * [vanitygaps](https://dwm.suckless.org/patches/vanitygaps/) 26 | * [xrdb](https://dwm.suckless.org/patches/xrdb/) 27 | 28 | 29 | ## installation, setup: 30 | ``` 31 | git clone https://github.com/BreadOnPenguins/dwm 32 | cd dwm 33 | sudo make clean install 34 | ``` 35 | 36 | * Basic ```~/.xinitrc``` requirement: ```exec dwm``` 37 | 38 | * Configure settings (fonts, bindings, gap pixels, etc) in **config.def.h** before compiling. 39 | - Defaults: Mod is bound to the windows key 40 | - ```mod + enter``` to open terminal 41 | - ```mod + q``` to quit window 42 | - ```mod + shift + backspace``` to fully exit 43 | 44 | 45 | I use [dwmblocks](https://github.com/torrinfail/dwmblocks) for my statusbar ([bar scripts](https://github.com/BreadOnPenguins/scripts)), included in ```~/.xprofile``` with ```exec dwmblocks```. 46 | If you intend to use another statusbar, [modify dwm appropriately](https://dwm.suckless.org/patches/anybar/) :) 47 | 48 | 49 | ## colors, other stuff: 50 | If you aren't using ```~/.Xresources``` with or without [pywal16](https://github.com/eylles/pywal16), default color palette is a variant of [Nord](https://www.nordtheme.com/). 51 | 52 | 53 | I have wal generate a template containing [dwm Xresource strings](https://dwm.suckless.org/patches/xrdb/). Then, I merge it with wal's auto-generated Xresources file, using ```xrdb -merge```. 54 | 55 | 56 | ```~/.config/wal/templates/xrdb_extra``` 57 | ``` 58 | dwm.normbordercolor: {color0} 59 | dwm.normbgcolor: {color0} 60 | dwm.normfgcolor: {color4} 61 | dwm.selbordercolor: {color8} 62 | dwm.selbgcolor: {color4} 63 | dwm.selfgcolor: {color0} 64 | ``` 65 | 66 | After creating the template, add these to your wal post-script for automatic xrdb merge and refresh. 67 | 68 | ``` 69 | ln -sf ~/.cache/wal/colors.Xresources ~/.Xresources 70 | cat ~/.Xresources ~/.cache/wal/xrdb_extra | xrdb -merge 71 | xdotool key super+ctrl+backslash # xrdb refresh keybind 72 | ``` 73 | 74 | Alternatively, if you prefer a different color-setting method, follow the instructions on [pywal16's wiki](https://github.com/eylles/pywal16/wiki/Customization#dwm). 75 | 76 | 77 | I use [slock](https://tools.suckless.org/slock/) for a lockscreen (build will be uploaded eventually), activated via keybind. 78 | 79 | My config has a few glyphs used cosmetically; for those to render properly, install a [font with extra glyphs](https://www.nerdfonts.com/#home). 80 | 81 | #### The [GNU Quilt](https://savannah.nongnu.org/projects/quilt/quilt/) system (used by Debian to manage patches in source packages) can be used to easily manage, apply, and reverse suckless software patches, and [this guide](https://codeberg.org/mok0/suckless-patches) (including ```suckless-patches.py```) can help download and prepare patches for use with Quilt. Thanks to [mok0](https://github.com/BreadOnPenguins/dwm/issues/1) for sharing! 82 | -------------------------------------------------------------------------------- /dwm.1: -------------------------------------------------------------------------------- 1 | .TH DWM 1 dwm\-VERSION 2 | .SH NAME 3 | dwm \- dynamic window manager 4 | .SH SYNOPSIS 5 | .B dwm 6 | .RB [ \-v ] 7 | .SH DESCRIPTION 8 | dwm is a dynamic window manager for X. It manages windows in tiled, monocle 9 | and floating layouts. Either layout can be applied dynamically, optimising the 10 | environment for the application in use and the task performed. 11 | .P 12 | In tiled layouts windows are managed in a master and stacking area. The master 13 | area on the left contains one window by default, and the stacking area on the 14 | right contains all other windows. The number of master area windows can be 15 | adjusted from zero to an arbitrary number. In monocle layout all windows are 16 | maximised to the screen size. In floating layout windows can be resized and 17 | moved freely. Dialog windows are always managed floating, regardless of the 18 | layout applied. 19 | .P 20 | Windows are grouped by tags. Each window can be tagged with one or multiple 21 | tags. Selecting certain tags displays all windows with these tags. 22 | .P 23 | Each screen contains a small status bar which displays all available tags, the 24 | layout, the title of the focused window, and the text read from the root window 25 | name property, if the screen is focused. A floating window is indicated with an 26 | empty square and a maximised floating window is indicated with a filled square 27 | before the windows title. The selected tags are indicated with a different 28 | color. The tags of the focused window are indicated with a filled square in the 29 | top left corner. The tags which are applied to one or more windows are 30 | indicated with an empty square in the top left corner. 31 | .P 32 | dwm draws a small border around windows to indicate the focus state. 33 | .SH OPTIONS 34 | .TP 35 | .B \-v 36 | prints version information to stderr, then exits. 37 | .SH USAGE 38 | .SS Status bar 39 | .TP 40 | .B X root window name 41 | is read and displayed in the status text area. It can be set with the 42 | .BR xsetroot (1) 43 | command. 44 | .TP 45 | .B Button1 46 | click on a tag label to display all windows with that tag, click on the layout 47 | label toggles between tiled and floating layout. 48 | .TP 49 | .B Button3 50 | click on a tag label adds/removes all windows with that tag to/from the view. 51 | .TP 52 | .B Mod1\-Button1 53 | click on a tag label applies that tag to the focused window. 54 | .TP 55 | .B Mod1\-Button3 56 | click on a tag label adds/removes that tag to/from the focused window. 57 | .SS Keyboard commands 58 | .TP 59 | .B Mod1\-Shift\-Return 60 | Start 61 | .BR st(1). 62 | .TP 63 | .B Mod1\-p 64 | Spawn 65 | .BR dmenu(1) 66 | for launching other programs. 67 | .TP 68 | .B Mod1\-, 69 | Focus previous screen, if any. 70 | .TP 71 | .B Mod1\-. 72 | Focus next screen, if any. 73 | .TP 74 | .B Mod1\-Shift\-, 75 | Send focused window to previous screen, if any. 76 | .TP 77 | .B Mod1\-Shift\-. 78 | Send focused window to next screen, if any. 79 | .TP 80 | .B Mod1\-b 81 | Toggles bar on and off. 82 | .TP 83 | .B Mod1\-t 84 | Sets tiled layout. 85 | .TP 86 | .B Mod1\-f 87 | Sets floating layout. 88 | .TP 89 | .B Mod1\-m 90 | Sets monocle layout. 91 | .TP 92 | .B Mod1\-space 93 | Toggles between current and previous layout. 94 | .TP 95 | .B Mod1\-j 96 | Focus next window. 97 | .TP 98 | .B Mod1\-k 99 | Focus previous window. 100 | .TP 101 | .B Mod1\-i 102 | Increase number of windows in master area. 103 | .TP 104 | .B Mod1\-d 105 | Decrease number of windows in master area. 106 | .TP 107 | .B Mod1\-l 108 | Increase master area size. 109 | .TP 110 | .B Mod1\-h 111 | Decrease master area size. 112 | .TP 113 | .B Mod1\-Return 114 | Zooms/cycles focused window to/from master area (tiled layouts only). 115 | .TP 116 | .B Mod1\-Shift\-c 117 | Close focused window. 118 | .TP 119 | .B Mod1\-Shift\-space 120 | Toggle focused window between tiled and floating state. 121 | .TP 122 | .B Mod1\-Tab 123 | Toggles to the previously selected tags. 124 | .TP 125 | .B Mod1\-Shift\-[1..n] 126 | Apply nth tag to focused window. 127 | .TP 128 | .B Mod1\-Shift\-0 129 | Apply all tags to focused window. 130 | .TP 131 | .B Mod1\-Control\-Shift\-[1..n] 132 | Add/remove nth tag to/from focused window. 133 | .TP 134 | .B Mod1\-[1..n] 135 | View all windows with nth tag. 136 | .TP 137 | .B Mod1\-0 138 | View all windows with any tag. 139 | .TP 140 | .B Mod1\-Control\-[1..n] 141 | Add/remove all windows with nth tag to/from the view. 142 | .TP 143 | .B Mod1\-Shift\-q 144 | Quit dwm. 145 | .TP 146 | .B Mod1\-Control\-Shift\-q 147 | Restart dwm. 148 | .SS Mouse commands 149 | .TP 150 | .B Mod1\-Button1 151 | Move focused window while dragging. Tiled windows will be toggled to the floating state. 152 | .TP 153 | .B Mod1\-Button2 154 | Toggles focused window between floating and tiled state. 155 | .TP 156 | .B Mod1\-Button3 157 | Resize focused window while dragging. Tiled windows will be toggled to the floating state. 158 | .SH CUSTOMIZATION 159 | dwm is customized by creating a custom config.h and (re)compiling the source 160 | code. This keeps it fast, secure and simple. 161 | .SH SIGNALS 162 | .TP 163 | .B SIGHUP - 1 164 | Restart the dwm process. 165 | .TP 166 | .B SIGTERM - 15 167 | Cleanly terminate the dwm process. 168 | .SH SEE ALSO 169 | .BR dmenu (1), 170 | .BR st (1) 171 | .SH ISSUES 172 | Java applications which use the XToolkit/XAWT backend may draw grey windows 173 | only. The XToolkit/XAWT backend breaks ICCCM-compliance in recent JDK 1.5 and early 174 | JDK 1.6 versions, because it assumes a reparenting window manager. Possible workarounds 175 | are using JDK 1.4 (which doesn't contain the XToolkit/XAWT backend) or setting the 176 | environment variable 177 | .BR AWT_TOOLKIT=MToolkit 178 | (to use the older Motif backend instead) or running 179 | .B xprop -root -f _NET_WM_NAME 32a -set _NET_WM_NAME LG3D 180 | or 181 | .B wmname LG3D 182 | (to pretend that a non-reparenting window manager is running that the 183 | XToolkit/XAWT backend can recognize) or when using OpenJDK setting the environment variable 184 | .BR _JAVA_AWT_WM_NONREPARENTING=1 . 185 | .SH BUGS 186 | Send all bug reports with a patch to hackers@suckless.org. 187 | -------------------------------------------------------------------------------- /vanitygaps.c: -------------------------------------------------------------------------------- 1 | /* Key binding functions */ 2 | static void defaultgaps(const Arg *arg); 3 | static void incrgaps(const Arg *arg); 4 | static void incrigaps(const Arg *arg); 5 | static void incrogaps(const Arg *arg); 6 | static void incrohgaps(const Arg *arg); 7 | static void incrovgaps(const Arg *arg); 8 | static void incrihgaps(const Arg *arg); 9 | static void incrivgaps(const Arg *arg); 10 | static void togglegaps(const Arg *arg); 11 | /* Layouts */ 12 | static void dwindle(Monitor *m); 13 | static void fibonacci(Monitor *m, int s); 14 | static void spiral(Monitor *m); 15 | static void tile(Monitor *m); 16 | /* Internals */ 17 | static void getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc); 18 | static void getfacts(Monitor *m, int msize, int ssize, float *mf, float *sf, int *mr, int *sr); 19 | static void setgaps(int oh, int ov, int ih, int iv); 20 | 21 | /* Settings */ 22 | #if !PERTAG_PATCH 23 | static int enablegaps = 1; 24 | #endif // PERTAG_PATCH 25 | 26 | void 27 | setgaps(int oh, int ov, int ih, int iv) 28 | { 29 | if (oh < 0) oh = 0; 30 | if (ov < 0) ov = 0; 31 | if (ih < 0) ih = 0; 32 | if (iv < 0) iv = 0; 33 | 34 | selmon->gappoh = oh; 35 | selmon->gappov = ov; 36 | selmon->gappih = ih; 37 | selmon->gappiv = iv; 38 | arrange(selmon); 39 | } 40 | 41 | void 42 | togglegaps(const Arg *arg) 43 | { 44 | #if PERTAG_PATCH 45 | selmon->pertag->enablegaps[selmon->pertag->curtag] = !selmon->pertag->enablegaps[selmon->pertag->curtag]; 46 | #else 47 | enablegaps = !enablegaps; 48 | #endif // PERTAG_PATCH 49 | arrange(NULL); 50 | } 51 | 52 | void 53 | defaultgaps(const Arg *arg) 54 | { 55 | setgaps(gappoh, gappov, gappih, gappiv); 56 | } 57 | 58 | void 59 | incrgaps(const Arg *arg) 60 | { 61 | setgaps( 62 | selmon->gappoh + arg->i, 63 | selmon->gappov + arg->i, 64 | selmon->gappih + arg->i, 65 | selmon->gappiv + arg->i 66 | ); 67 | } 68 | 69 | void 70 | incrigaps(const Arg *arg) 71 | { 72 | setgaps( 73 | selmon->gappoh, 74 | selmon->gappov, 75 | selmon->gappih + arg->i, 76 | selmon->gappiv + arg->i 77 | ); 78 | } 79 | 80 | void 81 | incrogaps(const Arg *arg) 82 | { 83 | setgaps( 84 | selmon->gappoh + arg->i, 85 | selmon->gappov + arg->i, 86 | selmon->gappih, 87 | selmon->gappiv 88 | ); 89 | } 90 | 91 | void 92 | incrohgaps(const Arg *arg) 93 | { 94 | setgaps( 95 | selmon->gappoh + arg->i, 96 | selmon->gappov, 97 | selmon->gappih, 98 | selmon->gappiv 99 | ); 100 | } 101 | 102 | void 103 | incrovgaps(const Arg *arg) 104 | { 105 | setgaps( 106 | selmon->gappoh, 107 | selmon->gappov + arg->i, 108 | selmon->gappih, 109 | selmon->gappiv 110 | ); 111 | } 112 | 113 | void 114 | incrihgaps(const Arg *arg) 115 | { 116 | setgaps( 117 | selmon->gappoh, 118 | selmon->gappov, 119 | selmon->gappih + arg->i, 120 | selmon->gappiv 121 | ); 122 | } 123 | 124 | void 125 | incrivgaps(const Arg *arg) 126 | { 127 | setgaps( 128 | selmon->gappoh, 129 | selmon->gappov, 130 | selmon->gappih, 131 | selmon->gappiv + arg->i 132 | ); 133 | } 134 | 135 | void 136 | getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc) 137 | { 138 | unsigned int n, oe, ie; 139 | #if PERTAG_PATCH 140 | oe = ie = selmon->pertag->enablegaps[selmon->pertag->curtag]; 141 | #else 142 | oe = ie = enablegaps; 143 | #endif // PERTAG_PATCH 144 | Client *c; 145 | 146 | for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); 147 | if (smartgaps && n == 1) { 148 | oe = 0; // outer gaps disabled when only one client 149 | } 150 | 151 | *oh = m->gappoh*oe; // outer horizontal gap 152 | *ov = m->gappov*oe; // outer vertical gap 153 | *ih = m->gappih*ie; // inner horizontal gap 154 | *iv = m->gappiv*ie; // inner vertical gap 155 | *nc = n; // number of clients 156 | } 157 | 158 | void 159 | getfacts(Monitor *m, int msize, int ssize, float *mf, float *sf, int *mr, int *sr) 160 | { 161 | unsigned int n; 162 | float mfacts, sfacts; 163 | int mtotal = 0, stotal = 0; 164 | Client *c; 165 | 166 | for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); 167 | mfacts = MIN(n, m->nmaster); 168 | sfacts = n - m->nmaster; 169 | 170 | for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) 171 | if (n < m->nmaster) 172 | mtotal += msize / mfacts; 173 | else 174 | stotal += ssize / sfacts; 175 | 176 | *mf = mfacts; // total factor of master area 177 | *sf = sfacts; // total factor of stack area 178 | *mr = msize - mtotal; // the remainder (rest) of pixels after an even master split 179 | *sr = ssize - stotal; // the remainder (rest) of pixels after an even stack split 180 | } 181 | 182 | /* Layouts */ 183 | 184 | /* Fibonacci layouts (spiral, dwindle) + gaps */ 185 | 186 | void 187 | fibonacci(Monitor *m, int s) 188 | { 189 | unsigned int i, n; 190 | int nx, ny, nw, nh; 191 | int oh, ov, ih, iv; 192 | int nv, hrest = 0, wrest = 0, r = 1; 193 | Client *c; 194 | 195 | getgaps(m, &oh, &ov, &ih, &iv, &n); 196 | if (n == 0) 197 | return; 198 | 199 | nx = m->wx + ov; 200 | ny = m->wy + oh; 201 | nw = m->ww - 2*ov; 202 | nh = m->wh - 2*oh; 203 | 204 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next)) { 205 | if (r) { 206 | if ((i % 2 && (nh - ih) / 2 <= (bh + 2*c->bw)) 207 | || (!(i % 2) && (nw - iv) / 2 <= (bh + 2*c->bw))) { 208 | r = 0; 209 | } 210 | if (r && i < n - 1) { 211 | if (i % 2) { 212 | nv = (nh - ih) / 2; 213 | hrest = nh - 2*nv - ih; 214 | nh = nv; 215 | } else { 216 | nv = (nw - iv) / 2; 217 | wrest = nw - 2*nv - iv; 218 | nw = nv; 219 | } 220 | 221 | if ((i % 4) == 2 && !s) 222 | nx += nw + iv; 223 | else if ((i % 4) == 3 && !s) 224 | ny += nh + ih; 225 | } 226 | 227 | if ((i % 4) == 0) { 228 | if (s) { 229 | ny += nh + ih; 230 | nh += hrest; 231 | } 232 | else { 233 | nh -= hrest; 234 | ny -= nh + ih; 235 | } 236 | } 237 | else if ((i % 4) == 1) { 238 | nx += nw + iv; 239 | nw += wrest; 240 | } 241 | else if ((i % 4) == 2) { 242 | ny += nh + ih; 243 | nh += hrest; 244 | if (i < n - 1) 245 | nw += wrest; 246 | } 247 | else if ((i % 4) == 3) { 248 | if (s) { 249 | nx += nw + iv; 250 | nw -= wrest; 251 | } else { 252 | nw -= wrest; 253 | nx -= nw + iv; 254 | nh += hrest; 255 | } 256 | } 257 | if (i == 0) { 258 | if (n != 1) { 259 | nw = (m->ww - iv - 2*ov) - (m->ww - iv - 2*ov) * (1 - m->mfact); 260 | wrest = 0; 261 | } 262 | ny = m->wy + oh; 263 | } 264 | else if (i == 1) 265 | nw = m->ww - nw - iv - 2*ov; 266 | i++; 267 | } 268 | 269 | resize(c, nx, ny, nw - (2*c->bw), nh - (2*c->bw), False); 270 | } 271 | } 272 | 273 | void 274 | dwindle(Monitor *m) 275 | { 276 | fibonacci(m, 1); 277 | } 278 | 279 | void 280 | spiral(Monitor *m) 281 | { 282 | fibonacci(m, 0); 283 | } 284 | 285 | 286 | /* Default tile layout + gaps */ 287 | 288 | static void 289 | tile(Monitor *m) 290 | { /* this is a calculation for how windows should be arranged for this layout */ 291 | unsigned int i, n; 292 | int oh, ov, ih, iv; 293 | int mx = 0, my = 0, mh = 0, mw = 0; 294 | int sx = 0, sy = 0, sh = 0, sw = 0; 295 | float mfacts, sfacts; 296 | int mrest, srest; 297 | Client *c; 298 | 299 | getgaps(m, &oh, &ov, &ih, &iv, &n); /* get gaps and # of tiled clients n) */ 300 | if (n == 0) /* don't bother arranging if there are no tiled windows */ 301 | return; 302 | 303 | sx = mx = m->wx + ov; 304 | sy = my = m->wy + oh; 305 | mh = m->wh - 2*oh - ih * (MIN(n, m->nmaster) - 1); /* height for master clients */ 306 | sh = m->wh - 2*oh - ih * (n - m->nmaster - 1); /* height for stack clients */ 307 | sw = mw = m->ww - 2*ov; 308 | 309 | if (m->nmaster && n > m->nmaster) { 310 | sw = (mw - iv) * (1 - m->mfact); /* stack width */ 311 | mw = mw - iv - sw; /* master width */ 312 | sx = mx + mw + iv; /* stack area x offset */ 313 | } 314 | 315 | getfacts(m, mh, sh, &mfacts, &sfacts, &mrest, &srest); /* per client scaling ratios: mfacts/sfacts, and remainder ratios */ 316 | 317 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) /* loop through all tiled clients */ 318 | if (i < m->nmaster) { /* resize master clients */ 319 | resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); 320 | my += HEIGHT(c) + ih; 321 | } else { /* resize stack clients */ 322 | resize(c, sx, sy, sw - (2*c->bw), (sh / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0); 323 | sy += HEIGHT(c) + ih; 324 | } 325 | } 326 | -------------------------------------------------------------------------------- /drw.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "drw.h" 9 | #include "util.h" 10 | 11 | #define UTF_INVALID 0xFFFD 12 | 13 | static int 14 | utf8decode(const char *s_in, long *u, int *err) 15 | { 16 | static const unsigned char lens[] = { 17 | /* 0XXXX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 18 | /* 10XXX */ 0, 0, 0, 0, 0, 0, 0, 0, /* invalid */ 19 | /* 110XX */ 2, 2, 2, 2, 20 | /* 1110X */ 3, 3, 21 | /* 11110 */ 4, 22 | /* 11111 */ 0, /* invalid */ 23 | }; 24 | static const unsigned char leading_mask[] = { 0x7F, 0x1F, 0x0F, 0x07 }; 25 | static const unsigned int overlong[] = { 0x0, 0x80, 0x0800, 0x10000 }; 26 | 27 | const unsigned char *s = (const unsigned char *)s_in; 28 | int len = lens[*s >> 3]; 29 | *u = UTF_INVALID; 30 | *err = 1; 31 | if (len == 0) 32 | return 1; 33 | 34 | long cp = s[0] & leading_mask[len - 1]; 35 | for (int i = 1; i < len; ++i) { 36 | if (s[i] == '\0' || (s[i] & 0xC0) != 0x80) 37 | return i; 38 | cp = (cp << 6) | (s[i] & 0x3F); 39 | } 40 | /* out of range, surrogate, overlong encoding */ 41 | if (cp > 0x10FFFF || (cp >> 11) == 0x1B || cp < overlong[len - 1]) 42 | return len; 43 | 44 | *err = 0; 45 | *u = cp; 46 | return len; 47 | } 48 | 49 | Drw * 50 | drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) 51 | { 52 | Drw *drw = ecalloc(1, sizeof(Drw)); 53 | 54 | drw->dpy = dpy; 55 | drw->screen = screen; 56 | drw->root = root; 57 | drw->w = w; 58 | drw->h = h; 59 | drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); 60 | drw->gc = XCreateGC(dpy, root, 0, NULL); 61 | XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); 62 | 63 | return drw; 64 | } 65 | 66 | void 67 | drw_resize(Drw *drw, unsigned int w, unsigned int h) 68 | { 69 | if (!drw) 70 | return; 71 | 72 | drw->w = w; 73 | drw->h = h; 74 | if (drw->drawable) 75 | XFreePixmap(drw->dpy, drw->drawable); 76 | drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); 77 | } 78 | 79 | void 80 | drw_free(Drw *drw) 81 | { 82 | XFreePixmap(drw->dpy, drw->drawable); 83 | XFreeGC(drw->dpy, drw->gc); 84 | drw_fontset_free(drw->fonts); 85 | free(drw); 86 | } 87 | 88 | /* This function is an implementation detail. Library users should use 89 | * drw_fontset_create instead. 90 | */ 91 | static Fnt * 92 | xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) 93 | { 94 | Fnt *font; 95 | XftFont *xfont = NULL; 96 | FcPattern *pattern = NULL; 97 | 98 | if (fontname) { 99 | /* Using the pattern found at font->xfont->pattern does not yield the 100 | * same substitution results as using the pattern returned by 101 | * FcNameParse; using the latter results in the desired fallback 102 | * behaviour whereas the former just results in missing-character 103 | * rectangles being drawn, at least with some fonts. */ 104 | if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { 105 | fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); 106 | return NULL; 107 | } 108 | if (!(pattern = FcNameParse((FcChar8 *) fontname))) { 109 | fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); 110 | XftFontClose(drw->dpy, xfont); 111 | return NULL; 112 | } 113 | } else if (fontpattern) { 114 | if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { 115 | fprintf(stderr, "error, cannot load font from pattern.\n"); 116 | return NULL; 117 | } 118 | } else { 119 | die("no font specified."); 120 | } 121 | 122 | font = ecalloc(1, sizeof(Fnt)); 123 | font->xfont = xfont; 124 | font->pattern = pattern; 125 | font->h = xfont->ascent + xfont->descent; 126 | font->dpy = drw->dpy; 127 | 128 | return font; 129 | } 130 | 131 | static void 132 | xfont_free(Fnt *font) 133 | { 134 | if (!font) 135 | return; 136 | if (font->pattern) 137 | FcPatternDestroy(font->pattern); 138 | XftFontClose(font->dpy, font->xfont); 139 | free(font); 140 | } 141 | 142 | Fnt* 143 | drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) 144 | { 145 | Fnt *cur, *ret = NULL; 146 | size_t i; 147 | 148 | if (!drw || !fonts) 149 | return NULL; 150 | 151 | for (i = 1; i <= fontcount; i++) { 152 | if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { 153 | cur->next = ret; 154 | ret = cur; 155 | } 156 | } 157 | return (drw->fonts = ret); 158 | } 159 | 160 | void 161 | drw_fontset_free(Fnt *font) 162 | { 163 | if (font) { 164 | drw_fontset_free(font->next); 165 | xfont_free(font); 166 | } 167 | } 168 | 169 | void 170 | drw_clr_create(Drw *drw, Clr *dest, const char *clrname) 171 | { 172 | if (!drw || !dest || !clrname) 173 | return; 174 | 175 | if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), 176 | DefaultColormap(drw->dpy, drw->screen), 177 | clrname, dest)) 178 | die("error, cannot allocate color '%s'", clrname); 179 | } 180 | 181 | /* Wrapper to create color schemes. The caller has to call free(3) on the 182 | * returned color scheme when done using it. */ 183 | Clr * 184 | drw_scm_create(Drw *drw, char *clrnames[], size_t clrcount) 185 | { 186 | size_t i; 187 | Clr *ret; 188 | 189 | /* need at least two colors for a scheme */ 190 | if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor)))) 191 | return NULL; 192 | 193 | for (i = 0; i < clrcount; i++) 194 | drw_clr_create(drw, &ret[i], clrnames[i]); 195 | return ret; 196 | } 197 | 198 | void 199 | drw_setfontset(Drw *drw, Fnt *set) 200 | { 201 | if (drw) 202 | drw->fonts = set; 203 | } 204 | 205 | void 206 | drw_setscheme(Drw *drw, Clr *scm) 207 | { 208 | if (drw) 209 | drw->scheme = scm; 210 | } 211 | 212 | void 213 | drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) 214 | { 215 | if (!drw || !drw->scheme) 216 | return; 217 | XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); 218 | if (filled) 219 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); 220 | else 221 | XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); 222 | } 223 | 224 | int 225 | drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) 226 | { 227 | int ty, ellipsis_x = 0; 228 | unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, hash, h0, h1; 229 | XftDraw *d = NULL; 230 | Fnt *usedfont, *curfont, *nextfont; 231 | int utf8strlen, utf8charlen, utf8err, render = x || y || w || h; 232 | long utf8codepoint = 0; 233 | const char *utf8str; 234 | FcCharSet *fccharset; 235 | FcPattern *fcpattern; 236 | FcPattern *match; 237 | XftResult result; 238 | int charexists = 0, overflow = 0; 239 | /* keep track of a couple codepoints for which we have no match. */ 240 | static unsigned int nomatches[128], ellipsis_width, invalid_width; 241 | static const char invalid[] = "�"; 242 | 243 | if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts) 244 | return 0; 245 | 246 | if (!render) { 247 | w = invert ? invert : ~invert; 248 | } else { 249 | XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); 250 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); 251 | if (w < lpad) 252 | return x + w; 253 | d = XftDrawCreate(drw->dpy, drw->drawable, 254 | DefaultVisual(drw->dpy, drw->screen), 255 | DefaultColormap(drw->dpy, drw->screen)); 256 | x += lpad; 257 | w -= lpad; 258 | } 259 | 260 | usedfont = drw->fonts; 261 | if (!ellipsis_width && render) 262 | ellipsis_width = drw_fontset_getwidth(drw, "..."); 263 | if (!invalid_width && render) 264 | invalid_width = drw_fontset_getwidth(drw, invalid); 265 | while (1) { 266 | ew = ellipsis_len = utf8err = utf8charlen = utf8strlen = 0; 267 | utf8str = text; 268 | nextfont = NULL; 269 | while (*text) { 270 | utf8charlen = utf8decode(text, &utf8codepoint, &utf8err); 271 | for (curfont = drw->fonts; curfont; curfont = curfont->next) { 272 | charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); 273 | if (charexists) { 274 | drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL); 275 | if (ew + ellipsis_width <= w) { 276 | /* keep track where the ellipsis still fits */ 277 | ellipsis_x = x + ew; 278 | ellipsis_w = w - ew; 279 | ellipsis_len = utf8strlen; 280 | } 281 | 282 | if (ew + tmpw > w) { 283 | overflow = 1; 284 | /* called from drw_fontset_getwidth_clamp(): 285 | * it wants the width AFTER the overflow 286 | */ 287 | if (!render) 288 | x += tmpw; 289 | else 290 | utf8strlen = ellipsis_len; 291 | } else if (curfont == usedfont) { 292 | text += utf8charlen; 293 | utf8strlen += utf8err ? 0 : utf8charlen; 294 | ew += utf8err ? 0 : tmpw; 295 | } else { 296 | nextfont = curfont; 297 | } 298 | break; 299 | } 300 | } 301 | 302 | if (overflow || !charexists || nextfont || utf8err) 303 | break; 304 | else 305 | charexists = 0; 306 | } 307 | 308 | if (utf8strlen) { 309 | if (render) { 310 | ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; 311 | XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], 312 | usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen); 313 | } 314 | x += ew; 315 | w -= ew; 316 | } 317 | if (utf8err && (!render || invalid_width < w)) { 318 | if (render) 319 | drw_text(drw, x, y, w, h, 0, invalid, invert); 320 | x += invalid_width; 321 | w -= invalid_width; 322 | } 323 | if (render && overflow) 324 | drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert); 325 | 326 | if (!*text || overflow) { 327 | break; 328 | } else if (nextfont) { 329 | charexists = 0; 330 | usedfont = nextfont; 331 | } else { 332 | /* Regardless of whether or not a fallback font is found, the 333 | * character must be drawn. */ 334 | charexists = 1; 335 | 336 | hash = (unsigned int)utf8codepoint; 337 | hash = ((hash >> 16) ^ hash) * 0x21F0AAAD; 338 | hash = ((hash >> 15) ^ hash) * 0xD35A2D97; 339 | h0 = ((hash >> 15) ^ hash) % LENGTH(nomatches); 340 | h1 = (hash >> 17) % LENGTH(nomatches); 341 | /* avoid expensive XftFontMatch call when we know we won't find a match */ 342 | if (nomatches[h0] == utf8codepoint || nomatches[h1] == utf8codepoint) 343 | goto no_match; 344 | 345 | fccharset = FcCharSetCreate(); 346 | FcCharSetAddChar(fccharset, utf8codepoint); 347 | 348 | if (!drw->fonts->pattern) { 349 | /* Refer to the comment in xfont_create for more information. */ 350 | die("the first font in the cache must be loaded from a font string."); 351 | } 352 | 353 | fcpattern = FcPatternDuplicate(drw->fonts->pattern); 354 | FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); 355 | FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); 356 | 357 | FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); 358 | FcDefaultSubstitute(fcpattern); 359 | match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); 360 | 361 | FcCharSetDestroy(fccharset); 362 | FcPatternDestroy(fcpattern); 363 | 364 | if (match) { 365 | usedfont = xfont_create(drw, NULL, match); 366 | if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { 367 | for (curfont = drw->fonts; curfont->next; curfont = curfont->next) 368 | ; /* NOP */ 369 | curfont->next = usedfont; 370 | } else { 371 | xfont_free(usedfont); 372 | nomatches[nomatches[h0] ? h1 : h0] = utf8codepoint; 373 | no_match: 374 | usedfont = drw->fonts; 375 | } 376 | } 377 | } 378 | } 379 | if (d) 380 | XftDrawDestroy(d); 381 | 382 | return x + (render ? w : 0); 383 | } 384 | 385 | void 386 | drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) 387 | { 388 | if (!drw) 389 | return; 390 | 391 | XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); 392 | XSync(drw->dpy, False); 393 | } 394 | 395 | unsigned int 396 | drw_fontset_getwidth(Drw *drw, const char *text) 397 | { 398 | if (!drw || !drw->fonts || !text) 399 | return 0; 400 | return drw_text(drw, 0, 0, 0, 0, 0, text, 0); 401 | } 402 | 403 | unsigned int 404 | drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n) 405 | { 406 | unsigned int tmp = 0; 407 | if (drw && drw->fonts && text && n) 408 | tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n); 409 | return MIN(n, tmp); 410 | } 411 | 412 | void 413 | drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) 414 | { 415 | XGlyphInfo ext; 416 | 417 | if (!font || !text) 418 | return; 419 | 420 | XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); 421 | if (w) 422 | *w = ext.xOff; 423 | if (h) 424 | *h = font->h; 425 | } 426 | 427 | Cur * 428 | drw_cur_create(Drw *drw, int shape) 429 | { 430 | Cur *cur; 431 | 432 | if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) 433 | return NULL; 434 | 435 | cur->cursor = XCreateFontCursor(drw->dpy, shape); 436 | 437 | return cur; 438 | } 439 | 440 | void 441 | drw_cur_free(Drw *drw, Cur *cursor) 442 | { 443 | if (!cursor) 444 | return; 445 | 446 | XFreeCursor(drw->dpy, cursor->cursor); 447 | free(cursor); 448 | } 449 | -------------------------------------------------------------------------------- /config.def.h: -------------------------------------------------------------------------------- 1 | /* 2 | bread's dwm build 3 | 4 | patches applied (incl. some slight changes): 5 | 6 | dwm-bartoggle-keybinds-6.4.diff ----- toggle pieces of the bar 7 | dwm-bulkill-20231029-9f88553.diff ----- kill all except current; kill all in tag 8 | dwm-colorbar-6.3.diff ----- fine control over statusbar colors 9 | dwm-fixmultimon-6.4.diff ----- pretty much a bug fix 10 | dwm-focusfullscreen-20211121-95e7342.diff ----- proper fullscreen 11 | dwm-focusmaster-return-6.2.diff ----- switch to master from anywhere in the stack 12 | dwm-focusmonmouse-6.2.diff ----- move mouse when you switch monitors via keybind 13 | dwm-hide_vacant_tags-6.4.diff ----- hide tags with no windows 14 | dwm-preventfocusshift-20240831-6.5.diff ----- automatically exit fullscreen when a window is spawned 15 | dwm-restartsig-20180523-6.2.diff ----- refresh dwm after recompile, without quitting 16 | dwm-spawntag-6.2.diff ----- spawn an application when tag is middle-clicked 17 | dwm-stacker-6.2.diff ----- more utilities to manage the stack 18 | dwm-statuscmd-20210405-67d76bd.diff ----- execute cmds when statusbar is clicked (used for dwmblocks) 19 | dwm-sticky-6.5.diff ----- make a window follow from tag to tag 20 | dwm-swallow-6.3.diff ----- terminal swallows launched programs 21 | dwm-vanitygaps-6.2.diff ----- gaps are functional: your eye is less inclined to drift while working. 22 | dwm-xrdb-6.4.diff - xresource database colors 23 | */ 24 | 25 | /* appearance */ 26 | static unsigned int borderpx = 1; /* border pixel of windows */ 27 | static unsigned int snap = 32; /* snap pixel */ 28 | static const unsigned int gappih = 20; /* horiz inner gap between windows */ 29 | static const unsigned int gappiv = 20; /* vert inner gap between windows */ 30 | static const unsigned int gappoh = 20; /* horiz outer gap between windows and screen edge */ 31 | static const unsigned int gappov = 30; /* vert outer gap between windows and screen edge */ 32 | static int smartgaps = 0; /* 1 means no outer gap when there is only one window */ 33 | static const int swallowfloating = 0; /* 1 means swallow floating windows by default */ 34 | static int showbar = 1; /* 0 means no bar */ 35 | static const int showtitle = 1; /* 0 means no title */ 36 | static const int showtags = 1; /* 0 means no tags */ 37 | static const int showlayout = 1; /* 0 means no layout indicator */ 38 | static const int showstatus = 1; /* 0 means no status bar */ 39 | static const int showfloating = 0; /* 0 means no floating indicator */ 40 | static int topbar = 1; /* 0 means bottom bar */ 41 | 42 | static char dmenufont[] = "monospace:size=10"; 43 | static const char *fonts[] = { "monospace:size=10", "Hack Nerd Font Mono:size=16", "NotoColorEmoji:pixelsize=14:antialias=true:autohint=true" }; 44 | 45 | /* default colors used if xrdb is not loaded */ 46 | static char normbgcolor[] = "#2e3440"; 47 | static char normbordercolor[] = "#4c566a"; 48 | static char normfgcolor[] = "#d8dee9"; 49 | static char selfgcolor[] = "#eceff4"; 50 | static char selbordercolor[] = "#a3be8c"; 51 | static char selbgcolor[] = "#b48ead"; 52 | 53 | static char *colors[][3] = { 54 | /* fg bg border */ 55 | [SchemeNorm] = { normfgcolor, normbgcolor, normbordercolor }, 56 | [SchemeSel] = { selbgcolor, selfgcolor, selbordercolor }, 57 | /* for bar --> {text, background, null} */ 58 | [SchemeStatus] = { normfgcolor, normbgcolor, normbgcolor }, /* status R */ 59 | [SchemeTagsSel] = { normfgcolor, normbgcolor, normbgcolor }, /* tag L selected */ 60 | [SchemeTagsNorm] = { selbordercolor, normbgcolor, normbgcolor }, /* tag L unselected */ 61 | [SchemeInfoSel] = { normfgcolor, normbgcolor, normbgcolor }, /* info M selected */ 62 | [SchemeInfoNorm] = { normfgcolor, normbgcolor, normbgcolor }, /* info M unselected */ 63 | }; 64 | 65 | 66 | /* tagging */ 67 | static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; 68 | // static const char *tags[] = { "󰎤", "󰎧", "󰎪", "󰎭", "󰎱", "󰎳", "󰎶", "󰎹", "󰎼" }; 69 | 70 | 71 | static const Rule rules[] = { 72 | /* xprop(1): 73 | * WM_CLASS(STRING) = instance, class 74 | * WM_NAME(STRING) = title 75 | */ 76 | /* class instance title tags mask isfloating isterminal noswallow monitor */ 77 | { "St", NULL, NULL, 0, 0, 1, 0, -1 }, 78 | { "fzfmenu", NULL, "fzf", 0, 1, 1, 1, -1 }, /* xev */ 79 | { NULL, NULL, "Event Tester", 0, 0, 0, 1, -1 }, /* xev */ 80 | }; 81 | 82 | #include "vanitygaps.c" 83 | 84 | 85 | /* layout(s) */ 86 | static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ 87 | static const int nmaster = 1; /* number of clients in master area */ 88 | static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ 89 | static const int lockfullscreen = 0; /* 1 will force focus on the fullscreen window */ 90 | 91 | 92 | static const Layout layouts[] = { /* alt glyphs: 󱡗 󱏋 */ 93 | /* symbol arrange function */ 94 | { "󰓒", tile }, /* first entry is default */ 95 | { "󰇥", NULL }, /* no layout function means floating behavior */ 96 | { "", monocle }, 97 | { "󰫣", spiral }, 98 | { "󰫥", dwindle }, 99 | }; 100 | 101 | 102 | /* key definitions */ 103 | #define MODKEY Mod4Mask // windows key 104 | #define TAGKEYS(KEY,TAG) \ 105 | { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ 106 | { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ 107 | { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ 108 | { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, 109 | #define STACKKEYS(MOD,ACTION) \ 110 | { MOD, XK_j, ACTION##stack, {.i = INC(+1) } }, \ 111 | { MOD, XK_k, ACTION##stack, {.i = INC(-1) } }, \ 112 | /*{ MOD, XK_grave, ACTION##stack, {.i = PREVSEL } }, \ 113 | { MOD, XK_q, ACTION##stack, {.i = 0 } }, \ 114 | { MOD, XK_a, ACTION##stack, {.i = 1 } }, \ 115 | { MOD, XK_z, ACTION##stack, {.i = 2 } }, \ 116 | { MOD, XK_x, ACTION##stack, {.i = -1 } }, */ 117 | 118 | /* helper for spawning shell commands in the pre dwm-5.0 fashion */ 119 | #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } 120 | 121 | /* helper for launching gtk application */ 122 | #define GTKCMD(cmd) { .v = (const char*[]){ "/usr/bin/gtk-launch", cmd, NULL } } 123 | 124 | #define STATUSBAR "dwmblocks" 125 | #define BROWSER "qutebrowser" 126 | 127 | /* commands */ 128 | static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */ 129 | static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", normbgcolor, "-nf", normfgcolor, "-sb", selbordercolor, "-sf", selfgcolor, NULL }; 130 | static const char *termcmd[] = { "st", NULL }; 131 | 132 | 133 | static const Arg tagexec[] = { /* spawn application when tag is middle-clicked */ 134 | { .v = termcmd }, /* 1 */ 135 | { .v = termcmd }, /* 2 */ 136 | { .v = termcmd }, /* 3 */ 137 | { .v = termcmd }, /* 4 */ 138 | { .v = termcmd }, /* 5 */ 139 | { .v = termcmd }, /* 6 */ 140 | { .v = termcmd }, /* 7 */ 141 | { .v = termcmd }, /* 8 */ 142 | { .v = termcmd }, /* 9 */ 143 | /* GTKCMD("gtkapplication") */ 144 | }; 145 | 146 | static const Key keys[] = { 147 | /* modifier key function argument */ 148 | { MODKEY, XK_d, spawn, {.v = dmenucmd } }, 149 | { MODKEY, XK_Return, spawn, {.v = termcmd } }, 150 | { MODKEY|ShiftMask, XK_b, togglebar, {0} }, 151 | STACKKEYS(MODKEY, focus) 152 | STACKKEYS(MODKEY|ShiftMask, push) 153 | { MODKEY|ShiftMask, XK_i, incnmaster, {.i = +1 } }, /* increase # of master windows */ 154 | { MODKEY|ControlMask, XK_i, incnmaster, {.i = -1 } }, /* decrease # of master windows */ 155 | { MODKEY, XK_h, setmfact, {.f = -0.05} }, 156 | { MODKEY, XK_l, setmfact, {.f = +0.05} }, 157 | 158 | 159 | { MODKEY, XK_Tab, view, {0} }, 160 | { MODKEY, XK_0, view, {.ui = ~0 } }, 161 | { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, 162 | 163 | 164 | { MODKEY|ControlMask|ShiftMask, XK_q, quit, {1} }, /* refresh dwm (restartsig) */ 165 | { MODKEY|ShiftMask, XK_BackSpace, quit, {0} }, /* quit dwm */ 166 | { MODKEY, XK_q, killclient, {0} }, /* quit window */ 167 | { MODKEY|ShiftMask, XK_q, killclient, {.ui = 1} }, /* kill all windows besides current */ 168 | /* { MODKEY|ShiftMask|ControlMask, XK_q, killclient, {.ui = 2} }, */ 169 | { MODKEY|ControlMask, XK_backslash, xrdb, {.v = NULL } }, /* refresh xrdb; run this when setting new colors */ 170 | 171 | 172 | { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, 173 | { MODKEY, XK_f, togglefullscreen, {0} }, /* focus fullscreen patch */ 174 | { MODKEY|ShiftMask, XK_m, setlayout, {.v = &layouts[2]} }, /* monacle */ 175 | { MODKEY, XK_s, setlayout, {.v = &layouts[3]} }, /* spiral */ 176 | { MODKEY|ShiftMask, XK_t, setlayout, {.v = &layouts[4]} }, /* dwindle */ 177 | { MODKEY|ControlMask, XK_space, setlayout, {0} }, 178 | { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, 179 | { MODKEY, XK_space, zoom, {0} }, 180 | { MODKEY|ControlMask, XK_space, focusmaster, {0} }, 181 | { MODKEY|ShiftMask, XK_s, togglesticky, {0} }, 182 | 183 | 184 | /* multi-monitor control */ 185 | { MODKEY, XK_bracketright, focusmon, {.i = -1 } }, 186 | { MODKEY|ShiftMask, XK_bracketright, tagmon, {.i = -1 } }, 187 | { MODKEY, XK_bracketleft, focusmon, {.i = +1 } }, 188 | { MODKEY|ShiftMask, XK_bracketleft, tagmon, {.i = +1 } }, 189 | 190 | /* gaps control */ 191 | { MODKEY, XK_minus, incrgaps, {.i = -3 } }, /* all */ 192 | { MODKEY, XK_equal, incrgaps, {.i = +3 } }, 193 | { MODKEY|Mod1Mask, XK_i, incrigaps, {.i = +1 } }, /* inner */ 194 | { MODKEY|Mod1Mask|ShiftMask, XK_i, incrigaps, {.i = -1 } }, 195 | { MODKEY|Mod1Mask, XK_o, incrogaps, {.i = +1 } }, /* outer */ 196 | { MODKEY|Mod1Mask|ShiftMask, XK_o, incrogaps, {.i = -1 } }, 197 | { MODKEY|Mod1Mask, XK_6, incrihgaps, {.i = +1 } }, /* inner horiz */ 198 | { MODKEY|Mod1Mask|ShiftMask, XK_6, incrihgaps, {.i = -1 } }, 199 | { MODKEY|Mod1Mask, XK_7, incrivgaps, {.i = +1 } }, /* inner vert */ 200 | { MODKEY|Mod1Mask|ShiftMask, XK_7, incrivgaps, {.i = -1 } }, 201 | { MODKEY|Mod1Mask, XK_8, incrohgaps, {.i = +1 } }, /* outer horiz */ 202 | { MODKEY|Mod1Mask|ShiftMask, XK_8, incrohgaps, {.i = -1 } }, 203 | { MODKEY|Mod1Mask, XK_9, incrovgaps, {.i = +1 } }, /* outer vert */ 204 | { MODKEY|Mod1Mask|ShiftMask, XK_9, incrovgaps, {.i = -1 } }, 205 | { MODKEY|ShiftMask, XK_equal, togglegaps, {0} }, 206 | { MODKEY|ShiftMask, XK_minus, defaultgaps, {0} }, 207 | 208 | 209 | /* tag keys */ 210 | TAGKEYS( XK_1, 0) 211 | TAGKEYS( XK_2, 1) 212 | TAGKEYS( XK_3, 2) 213 | TAGKEYS( XK_4, 3) 214 | TAGKEYS( XK_5, 4) 215 | TAGKEYS( XK_6, 5) 216 | TAGKEYS( XK_7, 6) 217 | TAGKEYS( XK_8, 7) 218 | TAGKEYS( XK_9, 8) 219 | 220 | 221 | /* toggle statusbar pieces individually */ 222 | { MODKEY|ControlMask, XK_t, togglebartitle, {0} }, 223 | { MODKEY|ControlMask, XK_s, togglebarstatus,{0} }, 224 | { MODKEY|ControlMask, XK_t, togglebartags, {0} }, 225 | { MODKEY|ControlMask, XK_e, togglebarcolor, {0} }, /* swaps fg/bg for tag+win */ 226 | { MODKEY|ControlMask, XK_r, togglebarlt, {0} }, 227 | { MODKEY|ControlMask, XK_f, togglebarfloat, {0} }, 228 | 229 | 230 | /* application bindings */ 231 | { MODKEY, XK_m, spawn, {.v = (const char*[]){ "st", "-e", "termusic", NULL } } }, 232 | { MODKEY, XK_w, spawn, {.v = (const char*[]){ BROWSER, NULL } } }, 233 | { MODKEY, XK_f, spawn, {.v = (const char*[]){ "st", "-e", "fff", NULL } } }, 234 | { MODKEY, XK_n, spawn, {.v = (const char*[]){ "st", "-e", "nvim", NULL } } }, 235 | { MODKEY|ShiftMask, XK_h, spawn, {.v = (const char*[]){ "st", "-e", "htop", NULL } } }, 236 | { MODKEY, XK_p, spawn, {.v = (const char*[]){ "darktable", NULL } } }, 237 | 238 | 239 | /* script launch bindings */ 240 | { MODKEY|ShiftMask, XK_n, spawn, {.v = (const char*[]){ "dmenunotes", NULL } } }, 241 | { MODKEY, XK_v, spawn, {.v = (const char*[]){ "cliphist", "sel", NULL } } }, 242 | { MODKEY, XK_c, spawn, {.v = (const char*[]){ "cliphist", "add", NULL } } }, 243 | { MODKEY|ShiftMask, XK_a, spawn, {.v = (const char*[]){ "dmenuvids", NULL } } }, 244 | { MODKEY|ControlMask, XK_a, spawn, {.v = (const char*[]){ "dmenuaudioswitch", NULL } } }, 245 | { MODKEY|ShiftMask, XK_d, spawn, {.v = (const char*[]){ "rip", NULL } } }, 246 | { MODKEY, XK_r, spawn, {.v = (const char*[]){ "rec", NULL } } }, 247 | { MODKEY|ShiftMask, XK_grave, spawn, {.v = (const char*[]){ "define", NULL } } }, 248 | { MODKEY|ShiftMask, XK_w, spawn, {.v = (const char*[]){ "wallpapermenu", NULL } } }, 249 | { MODKEY, XK_F1, spawn, SHCMD("screenshot") }, 250 | { MODKEY|ShiftMask, XK_F1, spawn, SHCMD("screenshot color") }, 251 | { MODKEY, XK_F2, spawn, {.v = (const char*[]){ "vb", NULL } } }, 252 | { MODKEY|ShiftMask, XK_F2, spawn, {.v = (const char*[]){ "dmenutemp", NULL } } }, 253 | { MODKEY, XK_F3, spawn, {.v = (const char*[]){ "phototransfer", NULL } } }, 254 | 255 | 256 | /* other bindings */ 257 | { MODKEY, XK_F12, spawn, SHCMD("playerctl -p termusic next") }, 258 | { MODKEY, XK_F11, spawn, SHCMD("playerctl -p termusic play-pause") }, 259 | { MODKEY|ShiftMask, XK_F11, spawn, SHCMD("playerctl play-pause") }, 260 | { MODKEY, XK_F10, spawn, SHCMD("playerctl -p termusic previous") }, 261 | { MODKEY|ShiftMask, XK_F8, spawn, SHCMD("slock systemctl suspend -i") }, 262 | { MODKEY, XK_F8, spawn, SHCMD("slock") }, 263 | { MODKEY, XK_F7, spawn, SHCMD("status-timer") }, 264 | { MODKEY|ShiftMask, XK_F7, spawn, SHCMD("status-timer cleanup") }, 265 | { MODKEY, XK_F6, spawn, SHCMD("amixer -D pulse sset Master 5%+") }, 266 | { MODKEY, XK_F5, spawn, SHCMD("amixer -D pulse sset Master 5%-") }, 267 | }; 268 | 269 | 270 | /* button definitions */ 271 | /* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ 272 | static const Button buttons[] = { 273 | /* click event mask button function argument */ 274 | #ifndef __OpenBSD__ 275 | { ClkWinTitle, 0, Button2, zoom, {0} }, 276 | { ClkStatusText, 0, Button1, sigstatusbar, {.i = 1} }, 277 | { ClkStatusText, 0, Button2, sigstatusbar, {.i = 2} }, 278 | { ClkStatusText, 0, Button3, sigstatusbar, {.i = 3} }, 279 | { ClkStatusText, 0, Button4, sigstatusbar, {.i = 4} }, 280 | { ClkStatusText, 0, Button5, sigstatusbar, {.i = 5} }, 281 | { ClkStatusText, ShiftMask, Button1, sigstatusbar, {.i = 6} }, 282 | #endif 283 | 284 | { ClkStatusText, ShiftMask, Button3, spawn, SHCMD("st -e nvim ~/.local/src/dwmblocks/blocks.h") }, 285 | { ClkClientWin, MODKEY, Button1, movemouse, {0} }, /* left click */ 286 | { ClkClientWin, MODKEY, Button2, defaultgaps, {0} }, /* middle click */ 287 | { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, /* right click */ 288 | { ClkClientWin, MODKEY, Button4, incrgaps, {.i = +1} }, /* scroll up */ 289 | { ClkClientWin, MODKEY, Button5, incrgaps, {.i = -1} }, /* scroll down */ 290 | { ClkTagBar, 0, Button1, view, {0} }, 291 | { ClkTagBar, 0, Button3, toggleview, {0} }, 292 | { ClkTagBar, MODKEY, Button1, tag, {0} }, 293 | { ClkTagBar, MODKEY, Button3, toggletag, {0} }, 294 | { ClkRootWin, 0, Button2, togglebar, {0} }, /* hide bar */ 295 | }; 296 | -------------------------------------------------------------------------------- /swallow: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | dwm - dynamic window manager | suckless.org software that sucks less 6 | 7 | 8 | 9 | 15 | 16 | 30 | 31 |
32 | 363 | 364 |
365 | 366 |

swallow

367 |

Description

368 |

This patch adds "window swallowing" to dwm as known from Plan 9's windowing 369 | system rio.

370 |

Clients marked with isterminal in config.h swallow a window opened by any 371 | child process, e.g. running xclock in a terminal. Closing the xclock window 372 | restores the terminal window in the current position.

373 |

This patch helps users spawning a lot of graphical programs from their command 374 | line by avoiding cluttering the screen with many unusable terminals. Being deep 375 | down in a directory hierarchy just does not make the use of dmenu feasible.

376 |

Dependencies

377 |
    378 |
  • libxcb
  • 379 |
  • Xlib-libxcb
  • 380 |
  • xcb-res
  • 381 |
382 |

These dependencies are needed due to the use of the latest revision of the X 383 | Resource Extension which is unsupported in vanilla Xlib.

384 |

Download

385 | 396 |

Notes

397 |

The window swallowing functionality requires dwm to walk the process tree, 398 | which is an inherently OS-specific task. Please contact one of the authors 399 | if you would like to help expand the list of supported operating systems.

400 |

Only terminals created by local processes can swallow windows, and only windows 401 | created by local processes can be swallowed.

402 |

Authors

403 | 412 |
413 | 414 |
415 | 416 | 417 | -------------------------------------------------------------------------------- /dwm.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. 2 | * 3 | * dynamic window manager is designed like any other X client as well. It is 4 | * driven through handling X events. In contrast to other X clients, a window 5 | * manager selects for SubstructureRedirectMask on the root window, to receive 6 | * events about window (dis-)appearance. Only one X connection at a time is 7 | * allowed to select for this event mask. 8 | * 9 | * The event handlers of dwm are organized in an array which is accessed 10 | * whenever a new event has been fetched. This allows event dispatching 11 | * in O(1) time. 12 | * 13 | * Each child of the root window is called a client, except windows which have 14 | * set the override_redirect flag. Clients are organized in a linked client 15 | * list on each monitor, the focus history is remembered through a stack list 16 | * on each monitor. Each client contains a bit array to indicate the tags of a 17 | * client. 18 | * 19 | * Keys and tagging rules are organized as arrays and defined in config.h. 20 | * 21 | * To understand everything else, start reading main(). 22 | */ 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #ifdef XINERAMA 41 | #include 42 | #endif /* XINERAMA - multihead */ 43 | #include 44 | #include 45 | #include 46 | #ifdef __OpenBSD__ 47 | #include 48 | #include 49 | #endif /* __OpenBSD */ 50 | 51 | #include "drw.h" 52 | #include "util.h" 53 | 54 | /* macros */ 55 | #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) 56 | #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) 57 | #define GETINC(X) ((X) - 2000) 58 | #define INC(X) ((X) + 2000) 59 | #define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ 60 | * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) 61 | #define ISINC(X) ((X) > 1000 && (X) < 3000) 62 | #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]) || C->issticky) 63 | #define PREVSEL 3000 64 | #define MOD(N,M) ((N)%(M) < 0 ? (N)%(M) + (M) : (N)%(M)) 65 | #define MOUSEMASK (BUTTONMASK|PointerMotionMask) 66 | #define WIDTH(X) ((X)->w + 2 * (X)->bw) 67 | #define HEIGHT(X) ((X)->h + 2 * (X)->bw) 68 | #define TAGMASK ((1 << LENGTH(tags)) - 1) 69 | #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) 70 | #define XRDB_LOAD_COLOR(R,V) if (XrmGetResource(xrdb, R, NULL, &type, &value) == True) { \ 71 | if (value.addr != NULL && strnlen(value.addr, 8) == 7 && value.addr[0] == '#') { \ 72 | int i = 1; \ 73 | for (; i <= 6; i++) { \ 74 | if (value.addr[i] < 48) break; \ 75 | if (value.addr[i] > 57 && value.addr[i] < 65) break; \ 76 | if (value.addr[i] > 70 && value.addr[i] < 97) break; \ 77 | if (value.addr[i] > 102) break; \ 78 | } \ 79 | if (i == 7) { \ 80 | strncpy(V, value.addr, 7); \ 81 | V[7] = '\0'; \ 82 | } \ 83 | } \ 84 | } 85 | #define TRUNC(X,A,B) (MAX((A), MIN((X), (B)))) 86 | 87 | 88 | /* enums */ 89 | enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ 90 | enum { SchemeNorm, SchemeSel, SchemeStatus, SchemeTagsSel, SchemeTagsNorm, SchemeInfoSel, SchemeInfoNorm }; /* color schemes */ 91 | enum { NetSupported, NetWMName, NetWMState, NetWMCheck, 92 | NetWMFullscreen, NetWMSticky, NetActiveWindow, NetWMWindowType, 93 | NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms (extended window manager hints) */ 94 | enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ 95 | enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, 96 | ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ 97 | 98 | typedef union { 99 | int i; /* integer argument */ 100 | unsigned int ui; /* unsigned integer */ 101 | float f; /* float */ 102 | const void *v; /* generic pointer */ 103 | } Arg; 104 | 105 | typedef struct { /* mouse button interactions */ 106 | unsigned int click; /* where the click happened */ 107 | unsigned int mask; /* modifier keys */ 108 | unsigned int button; /* the mouse button used */ 109 | void (*func)(const Arg *arg); /* a pointer to the function that should be executed */ 110 | const Arg arg; /* argument to pass into the function */ 111 | } Button; 112 | 113 | typedef struct Monitor Monitor; 114 | typedef struct Client Client; 115 | struct Client { /* a window that dwm is managing */ 116 | char name[256]; /* window's title as shown in bar */ 117 | float mina, maxa; /* min and max aspect ratios when resizing windows */ 118 | int x, y, w, h; /* current pos of window */ 119 | int oldx, oldy, oldw, oldh; /* prev pos of window */ 120 | int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid; /* size hints */ 121 | int bw, oldbw; /* current and prev border widths */ 122 | unsigned int tags; /* bitmasks for which tags window is visible on */ 123 | int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, issticky, isterminal, noswallow; /* window states */ 124 | pid_t pid; /* pid of application in window - useful for swallowing */ 125 | Client *next; /* next client, in the linked list of all clients */ 126 | Client *snext; /* next in the STACK */ 127 | Client *swallowing; /* points to the client this one is swallowing (swallow patch) */ 128 | Monitor *mon; /* the monitor this client is on */ 129 | Window win; /* X11 win id */ 130 | }; 131 | 132 | typedef struct { /* key press interactions */ 133 | unsigned int mod; 134 | KeySym keysym; 135 | void (*func)(const Arg *); 136 | const Arg arg; 137 | } Key; 138 | 139 | typedef struct { /* layouts */ 140 | const char *symbol; 141 | void (*arrange)(Monitor *); 142 | } Layout; 143 | 144 | struct Monitor { 145 | char ltsymbol[16]; 146 | float mfact; 147 | int nmaster; 148 | int num; 149 | int by; /* bar geometry */ 150 | int mx, my, mw, mh; /* screen size */ 151 | int wx, wy, ww, wh; /* window area */ 152 | int gappih; /* horizontal gap between windows */ 153 | int gappiv; /* vertical gap between windows */ 154 | int gappoh; /* horizontal outer gaps */ 155 | int gappov; /* vertical outer gaps */ 156 | unsigned int seltags; 157 | unsigned int sellt; 158 | unsigned int tagset[2]; 159 | int showbar; 160 | int showtitle; 161 | int showtags; 162 | int showlayout; 163 | int showstatus; 164 | int showfloating; 165 | int topbar; 166 | Client *clients; 167 | Client *sel; 168 | Client *stack; 169 | Client *tagmarked[32]; 170 | Monitor *next; 171 | Window barwin; 172 | const Layout *lt[2]; 173 | }; 174 | 175 | typedef struct { 176 | const char *class; 177 | const char *instance; 178 | const char *title; 179 | unsigned int tags; 180 | int isfloating; 181 | int isterminal; 182 | int noswallow; 183 | int monitor; 184 | } Rule; 185 | 186 | 187 | /* function declarations */ 188 | static void applyrules(Client *c); 189 | static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); 190 | static void arrange(Monitor *m); 191 | static void arrangemon(Monitor *m); 192 | static void attach(Client *c); 193 | static void attachstack(Client *c); 194 | static void buttonpress(XEvent *e); 195 | static void checkotherwm(void); 196 | static void cleanup(void); 197 | static void cleanupmon(Monitor *mon); 198 | static void clientmessage(XEvent *e); 199 | static void configure(Client *c); 200 | static void configurenotify(XEvent *e); 201 | static void configurerequest(XEvent *e); 202 | static Monitor *createmon(void); 203 | static void destroynotify(XEvent *e); 204 | static void detach(Client *c); 205 | static void detachstack(Client *c); 206 | static Monitor *dirtomon(int dir); 207 | static void drawbar(Monitor *m); 208 | static void drawbars(void); 209 | static void enternotify(XEvent *e); 210 | static void expose(XEvent *e); 211 | static void focus(Client *c); 212 | static void focusin(XEvent *e); 213 | static void focusmaster(const Arg *arg); 214 | static void focusmon(const Arg *arg); 215 | static void focusstack(const Arg *arg); 216 | static Atom getatomprop(Client *c, Atom prop); 217 | static int getrootptr(int *x, int *y); 218 | static long getstate(Window w); 219 | static pid_t getstatusbarpid(void); 220 | static void sigstatusbar(const Arg *arg); 221 | static int gettextprop(Window w, Atom atom, char *text, unsigned int size); 222 | static void grabbuttons(Client *c, int focused); 223 | static void grabkeys(void); 224 | static void incnmaster(const Arg *arg); 225 | static void keypress(XEvent *e); 226 | static void killthis(Client *c); 227 | static void killclient(const Arg *arg); 228 | static void loadxrdb(void); 229 | static void manage(Window w, XWindowAttributes *wa); 230 | static void mappingnotify(XEvent *e); 231 | static void maprequest(XEvent *e); 232 | static void monocle(Monitor *m); 233 | static void motionnotify(XEvent *e); 234 | static void movemouse(const Arg *arg); 235 | static Client *nexttiled(Client *c); 236 | static void pop(Client *c); 237 | static void propertynotify(XEvent *e); 238 | static void pushstack(const Arg *arg); 239 | static void quit(const Arg *arg); 240 | static Monitor *recttomon(int x, int y, int w, int h); 241 | static void resize(Client *c, int x, int y, int w, int h, int interact); 242 | static void resizeclient(Client *c, int x, int y, int w, int h); 243 | static void resizemouse(const Arg *arg); 244 | static void restack(Monitor *m); 245 | static void run(void); 246 | static void scan(void); 247 | static int sendevent(Client *c, Atom proto); 248 | static void sendmon(Client *c, Monitor *m); 249 | static void setclientstate(Client *c, long state); 250 | static void setfocus(Client *c); 251 | static void setfullscreen(Client *c, int fullscreen); 252 | static void setsticky(Client *c, int sticky); 253 | static void setlayout(const Arg *arg); 254 | static void setmfact(const Arg *arg); 255 | static void setup(void); 256 | static void seturgent(Client *c, int urg); 257 | static void showhide(Client *c); 258 | static void spawn(const Arg *arg); 259 | static void sighup(int unused); 260 | static void sigterm(int unused); 261 | static int stackpos(const Arg *arg); 262 | static void tag(const Arg *arg); 263 | static void tagmon(const Arg *arg); 264 | static void togglebar(const Arg *arg); 265 | static void togglebarcolor(const Arg *arg); 266 | static void togglebartags(const Arg *arg); 267 | static void togglebartitle(const Arg *arg); 268 | static void togglebarlt(const Arg *arg); 269 | static void togglebarstatus(const Arg *arg); 270 | static void togglebarfloat(const Arg *arg); 271 | static void togglefloating(const Arg *arg); 272 | static void togglefullscreen(const Arg *arg); 273 | static void togglesticky(const Arg *arg); 274 | static void toggletag(const Arg *arg); 275 | static void toggleview(const Arg *arg); 276 | static void unfocus(Client *c, int setfocus); 277 | static void unmanage(Client *c, int destroyed); 278 | static void unmapnotify(XEvent *e); 279 | static void updatebarpos(Monitor *m); 280 | static void updatebars(void); 281 | static void updateclientlist(void); 282 | static int updategeom(void); 283 | static void updatenumlockmask(void); 284 | static void updatesizehints(Client *c); 285 | static void updatestatus(void); 286 | static void updatetitle(Client *c); 287 | static void updatewindowtype(Client *c); 288 | static void updatewmhints(Client *c); 289 | static void view(const Arg *arg); 290 | static Client *wintoclient(Window w); 291 | static Monitor *wintomon(Window w); 292 | static int xerror(Display *dpy, XErrorEvent *ee); 293 | static int xerrordummy(Display *dpy, XErrorEvent *ee); 294 | static int xerrorstart(Display *dpy, XErrorEvent *ee); 295 | static void xrdb(const Arg *arg); 296 | static void zoom(const Arg *arg); 297 | 298 | static pid_t getparentprocess(pid_t p); 299 | static int isdescprocess(pid_t p, pid_t c); 300 | static Client *swallowingclient(Window w); 301 | static Client *termforwin(const Client *c); 302 | static pid_t winpid(Window w); 303 | 304 | /* variables */ 305 | static const char broken[] = "broken"; 306 | static char stext[256]; 307 | static int statusw; 308 | static int statussig; 309 | static pid_t statuspid = -1; 310 | static int screen; 311 | static int sw, sh; /* X display screen geometry width, height */ 312 | static int bh; /* bar height */ 313 | static int lrpad; /* sum of left and right padding for text */ 314 | static int (*xerrorxlib)(Display *, XErrorEvent *); 315 | static unsigned int numlockmask = 0; 316 | static void (*handler[LASTEvent]) (XEvent *) = { /* maps X event type to matching function */ 317 | [ButtonPress] = buttonpress, /* mouse button */ 318 | [ClientMessage] = clientmessage, /* messages sent from windows, like fullscreen toggle */ 319 | [ConfigureRequest] = configurerequest, /* window (client) asks to change geometry */ 320 | [ConfigureNotify] = configurenotify, /* root window geom changes */ 321 | [DestroyNotify] = destroynotify, /* window closed - removes client from dwm */ 322 | [EnterNotify] = enternotify, /* mouse enters a window */ 323 | [Expose] = expose, /* a part of the window needs to be redrawn */ 324 | [FocusIn] = focusin, 325 | [KeyPress] = keypress, /* keyboard */ 326 | [MappingNotify] = mappingnotify, /* keyboard mapping change */ 327 | [MapRequest] = maprequest, /* a new window needs to be mapped */ 328 | [MotionNotify] = motionnotify, /* mouse movement */ 329 | [PropertyNotify] = propertynotify, /* window property changes */ 330 | [UnmapNotify] = unmapnotify /* window needs to be unmapped */ 331 | }; 332 | static Atom wmatom[WMLast], netatom[NetLast]; 333 | static int restart = 0; 334 | static int running = 1; 335 | static Cur *cursor[CurLast]; 336 | static Clr **scheme; 337 | static Display *dpy; 338 | static Drw *drw; 339 | static Monitor *mons, *selmon; 340 | static Window root, wmcheckwin; 341 | 342 | static xcb_connection_t *xcon; 343 | 344 | /* configuration, allows nested code to access above variables */ 345 | #include "config.h" 346 | 347 | /* compile-time check if all tags fit into an unsigned int bit array. */ 348 | struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; 349 | 350 | /* function implementations */ 351 | void 352 | applyrules(Client *c) 353 | { 354 | const char *class, *instance; 355 | unsigned int i; 356 | const Rule *r; 357 | Monitor *m; 358 | XClassHint ch = { NULL, NULL }; 359 | 360 | /* rule matching */ 361 | c->isfloating = 0; 362 | c->tags = 0; 363 | XGetClassHint(dpy, c->win, &ch); 364 | class = ch.res_class ? ch.res_class : broken; 365 | instance = ch.res_name ? ch.res_name : broken; 366 | 367 | for (i = 0; i < LENGTH(rules); i++) { 368 | r = &rules[i]; 369 | if ((!r->title || strstr(c->name, r->title)) 370 | && (!r->class || strstr(class, r->class)) 371 | && (!r->instance || strstr(instance, r->instance))) 372 | { 373 | c->isterminal = r->isterminal; 374 | c->noswallow = r->noswallow; 375 | c->isfloating = r->isfloating; 376 | c->tags |= r->tags; 377 | for (m = mons; m && m->num != r->monitor; m = m->next); 378 | if (m) 379 | c->mon = m; 380 | } 381 | } 382 | if (ch.res_class) 383 | XFree(ch.res_class); 384 | if (ch.res_name) 385 | XFree(ch.res_name); 386 | c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; 387 | } 388 | 389 | int 390 | applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) 391 | { 392 | int baseismin; 393 | Monitor *m = c->mon; 394 | 395 | /* set minimum possible */ 396 | *w = MAX(1, *w); 397 | *h = MAX(1, *h); 398 | if (interact) { 399 | if (*x > sw) 400 | *x = sw - WIDTH(c); 401 | if (*y > sh) 402 | *y = sh - HEIGHT(c); 403 | if (*x + *w + 2 * c->bw < 0) 404 | *x = 0; 405 | if (*y + *h + 2 * c->bw < 0) 406 | *y = 0; 407 | } else { 408 | if (*x >= m->wx + m->ww) 409 | *x = m->wx + m->ww - WIDTH(c); 410 | if (*y >= m->wy + m->wh) 411 | *y = m->wy + m->wh - HEIGHT(c); 412 | if (*x + *w + 2 * c->bw <= m->wx) 413 | *x = m->wx; 414 | if (*y + *h + 2 * c->bw <= m->wy) 415 | *y = m->wy; 416 | } 417 | if (*h < bh) 418 | *h = bh; 419 | if (*w < bh) 420 | *w = bh; 421 | if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { 422 | if (!c->hintsvalid) 423 | updatesizehints(c); 424 | /* see last two sentences in ICCCM 4.1.2.3 */ 425 | baseismin = c->basew == c->minw && c->baseh == c->minh; 426 | if (!baseismin) { /* temporarily remove base dimensions */ 427 | *w -= c->basew; 428 | *h -= c->baseh; 429 | } 430 | /* adjust for aspect limits */ 431 | if (c->mina > 0 && c->maxa > 0) { 432 | if (c->maxa < (float)*w / *h) 433 | *w = *h * c->maxa + 0.5; 434 | else if (c->mina < (float)*h / *w) 435 | *h = *w * c->mina + 0.5; 436 | } 437 | if (baseismin) { /* increment calculation requires this */ 438 | *w -= c->basew; 439 | *h -= c->baseh; 440 | } 441 | /* adjust for increment value */ 442 | if (c->incw) 443 | *w -= *w % c->incw; 444 | if (c->inch) 445 | *h -= *h % c->inch; 446 | /* restore base dimensions */ 447 | *w = MAX(*w + c->basew, c->minw); 448 | *h = MAX(*h + c->baseh, c->minh); 449 | if (c->maxw) 450 | *w = MIN(*w, c->maxw); 451 | if (c->maxh) 452 | *h = MIN(*h, c->maxh); 453 | } 454 | return *x != c->x || *y != c->y || *w != c->w || *h != c->h; 455 | } 456 | 457 | void 458 | arrange(Monitor *m) /* if a specific monitor *m is given, it arranges that one. if NULL, it loops thru all */ 459 | { 460 | XEvent ev; /* declares a generic X event */ 461 | if (m) 462 | showhide(m->stack); /* show visible windows and hide others */ 463 | else for (m = mons; m; m = m->next) 464 | showhide(m->stack); 465 | if (m) { /* if *m is a single monitor */ 466 | arrangemon(m); /* applies the current layout to windows (tile, spiral, etc)*/ 467 | restack(m); /* proper stacking order --> focused window on top */ 468 | } else { /* else if *m was NULL */ 469 | for (m = mons; m; m = m->next) /* loop through all monitors */ 470 | arrangemon(m); /* apply layout to each monitor */ 471 | XSync(dpy, False); /* ensure X server processed all commands (cleaning up) */ 472 | while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); 473 | } 474 | } 475 | 476 | void 477 | arrangemon(Monitor *m) 478 | { /* applies current layout to window on monitor *m */ 479 | strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); /* copies layout symbol string to show in bar */ 480 | if (m->lt[m->sellt]->arrange) /* if the layout has an associated arrange func, call it (ex. tile() )*/ 481 | m->lt[m->sellt]->arrange(m); 482 | } 483 | 484 | void 485 | attach(Client *c) 486 | { 487 | c->next = c->mon->clients; 488 | c->mon->clients = c; 489 | } 490 | 491 | void 492 | attachstack(Client *c) 493 | { 494 | c->snext = c->mon->stack; 495 | c->mon->stack = c; 496 | } 497 | 498 | void 499 | swallow(Client *p, Client *c) 500 | { 501 | 502 | if (c->noswallow || c->isterminal) 503 | return; 504 | if (c->noswallow && !swallowfloating && c->isfloating) 505 | return; 506 | 507 | detach(c); 508 | detachstack(c); 509 | 510 | setclientstate(c, WithdrawnState); 511 | XUnmapWindow(dpy, p->win); 512 | 513 | p->swallowing = c; 514 | c->mon = p->mon; 515 | 516 | Window w = p->win; 517 | p->win = c->win; 518 | c->win = w; 519 | updatetitle(p); 520 | XMoveResizeWindow(dpy, p->win, p->x, p->y, p->w, p->h); 521 | arrange(p->mon); 522 | configure(p); 523 | updateclientlist(); 524 | } 525 | 526 | void 527 | unswallow(Client *c) 528 | { 529 | c->win = c->swallowing->win; 530 | 531 | free(c->swallowing); 532 | c->swallowing = NULL; 533 | 534 | /* unfullscreen the client */ 535 | setfullscreen(c, 0); 536 | updatetitle(c); 537 | arrange(c->mon); 538 | XMapWindow(dpy, c->win); 539 | XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); 540 | setclientstate(c, NormalState); 541 | focus(NULL); 542 | arrange(c->mon); 543 | } 544 | 545 | void 546 | buttonpress(XEvent *e) 547 | { /* handles clickable areas on the bar */ 548 | unsigned int i, x, click; 549 | Arg arg = {0}; 550 | Client *c; 551 | Monitor *m; 552 | XButtonPressedEvent *ev = &e->xbutton; 553 | char *text, *s, ch; 554 | 555 | click = ClkRootWin; 556 | /* focus monitor if necessary */ 557 | if ((m = wintomon(ev->window)) && m != selmon) { 558 | unfocus(selmon->sel, 1); 559 | selmon = m; 560 | focus(NULL); 561 | } 562 | if (ev->window == selmon->barwin) { 563 | i = x = 0; 564 | unsigned int occ = 0; 565 | for(c = m->clients; c; c=c->next) 566 | occ |= c->tags == TAGMASK ? 0 : c->tags; 567 | do { 568 | /* do not reserve space for vacant tags */ 569 | if (!(occ & 1 << i || m->tagset[m->seltags] & 1 << i)) 570 | continue; 571 | if (selmon->showtags) 572 | x += TEXTW(tags[i]); 573 | } while (ev->x >= x && ++i < LENGTH(tags)); 574 | if (i < LENGTH(tags) && selmon->showtags) { 575 | click = ClkTagBar; 576 | arg.ui = 1 << i; 577 | } else if (ev->x < x + TEXTW(selmon->ltsymbol) && selmon->showlayout) 578 | click = ClkLtSymbol; 579 | else if (ev->x > selmon->ww - statusw && selmon->showstatus) { 580 | x = selmon->ww - statusw; 581 | click = ClkStatusText; 582 | statussig = 0; /* statuscmd stuff */ 583 | for (text = s = stext; *s && x <= ev->x; s++) { /* loop through to determine which block was clicked */ 584 | if ((unsigned char)(*s) < ' ') { /* sig delim, if block boundaries are off check this */ 585 | ch = *s; /* measure width */ 586 | *s = '\0'; 587 | x += TEXTW(text) - lrpad; /* debug test try (lrpad / 2) */ 588 | *s = ch; 589 | text = s + 1; /* move text to next char after delim */ 590 | if (x >= ev->x) /* check click pos */ 591 | break; 592 | statussig = ch; /* save control char as sig # */ 593 | } 594 | } 595 | } else if (selmon->showtitle) { 596 | statussig = 0; 597 | for (text = s = stext; *s && x <= ev->x; s++) { 598 | if ((unsigned char)(*s) < ' ') { 599 | ch = *s; 600 | *s = '\0'; 601 | x += TEXTW(text) - lrpad; 602 | *s = ch; 603 | text = s + 1; 604 | if (x >= ev->x) 605 | break; 606 | statussig = ch; 607 | } 608 | } 609 | } 610 | } else 611 | click = ClkWinTitle; 612 | if ((c = wintoclient(ev->window))) { 613 | focus(c); 614 | restack(selmon); 615 | XAllowEvents(dpy, ReplayPointer, CurrentTime); 616 | click = ClkClientWin; 617 | } 618 | for (i = 0; i < LENGTH(buttons); i++) 619 | if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button 620 | && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) 621 | buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); 622 | } 623 | 624 | void 625 | checkotherwm(void) 626 | { 627 | xerrorxlib = XSetErrorHandler(xerrorstart); 628 | /* this causes an error if some other window manager is running */ 629 | XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); 630 | XSync(dpy, False); 631 | XSetErrorHandler(xerror); 632 | XSync(dpy, False); 633 | } 634 | 635 | void 636 | cleanup(void) 637 | { /* called when dwm exits normally or thru restart */ 638 | Arg a = {.ui = ~0}; /* switches to viewing all windows before shutdown */ 639 | Layout foo = { "", NULL }; /* switches layout to NULL so no more layout logic is ran */ 640 | Monitor *m; 641 | size_t i; 642 | 643 | view(&a); 644 | selmon->lt[selmon->sellt] = &foo; 645 | for (m = mons; m; m = m->next) /* loop through every monitor */ 646 | while (m->stack) /* call unmanage on every client window in the stack */ 647 | unmanage(m->stack, 0); 648 | XUngrabKey(dpy, AnyKey, AnyModifier, root); /* releases all X keybindings */ 649 | while (mons) 650 | cleanupmon(mons); 651 | for (i = 0; i < CurLast; i++) 652 | drw_cur_free(drw, cursor[i]); 653 | for (i = 0; i < LENGTH(colors); i++) 654 | free(scheme[i]); 655 | free(scheme); 656 | XDestroyWindow(dpy, wmcheckwin); 657 | drw_free(drw); 658 | XSync(dpy, False); /* flushes all X requests */ 659 | XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); /* reverts keyboard focus to root win */ 660 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); 661 | } 662 | 663 | void 664 | cleanupmon(Monitor *mon) 665 | { 666 | Monitor *m; 667 | 668 | if (mon == mons) 669 | mons = mons->next; 670 | else { 671 | for (m = mons; m && m->next != mon; m = m->next); 672 | m->next = mon->next; 673 | } 674 | XUnmapWindow(dpy, mon->barwin); /* hides statusbar associated w/ monitor */ 675 | XDestroyWindow(dpy, mon->barwin); /* deletes bar window from X server entirely */ 676 | free(mon); /* free memory */ 677 | } 678 | 679 | void 680 | clientmessage(XEvent *e) 681 | { 682 | XClientMessageEvent *cme = &e->xclient; 683 | Client *c = wintoclient(cme->window); 684 | 685 | if (!c) 686 | return; 687 | if (cme->message_type == netatom[NetWMState]) { 688 | if (cme->data.l[1] == netatom[NetWMFullscreen] 689 | || cme->data.l[2] == netatom[NetWMFullscreen]) 690 | setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ 691 | || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); 692 | 693 | if (cme->data.l[1] == netatom[NetWMSticky] 694 | || cme->data.l[2] == netatom[NetWMSticky]) 695 | setsticky(c, (cme->data.l[0] == 1 || (cme->data.l[0] == 2 && !c->issticky))); 696 | } else if (cme->message_type == netatom[NetActiveWindow]) { 697 | if (c != selmon->sel && !c->isurgent) 698 | seturgent(c, 1); 699 | } 700 | } 701 | 702 | void 703 | configure(Client *c) 704 | { 705 | XConfigureEvent ce; 706 | 707 | ce.type = ConfigureNotify; 708 | ce.display = dpy; 709 | ce.event = c->win; 710 | ce.window = c->win; 711 | ce.x = c->x; 712 | ce.y = c->y; 713 | ce.width = c->w; 714 | ce.height = c->h; 715 | ce.border_width = c->bw; 716 | ce.above = None; 717 | ce.override_redirect = False; 718 | XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); 719 | } 720 | 721 | void 722 | configurenotify(XEvent *e) 723 | { 724 | Monitor *m; 725 | Client *c; 726 | XConfigureEvent *ev = &e->xconfigure; 727 | int dirty; 728 | 729 | /* TODO: updategeom handling sucks, needs to be simplified */ 730 | if (ev->window == root) { 731 | dirty = (sw != ev->width || sh != ev->height); 732 | sw = ev->width; 733 | sh = ev->height; 734 | if (updategeom() || dirty) { 735 | drw_resize(drw, sw, bh); 736 | updatebars(); 737 | for (m = mons; m; m = m->next) { 738 | for (c = m->clients; c; c = c->next) 739 | if (c->isfullscreen) 740 | resizeclient(c, m->mx, m->my, m->mw, m->mh); 741 | XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); 742 | } 743 | focus(NULL); 744 | arrange(NULL); 745 | } 746 | } 747 | } 748 | 749 | void 750 | configurerequest(XEvent *e) 751 | { 752 | Client *c; 753 | Monitor *m; 754 | XConfigureRequestEvent *ev = &e->xconfigurerequest; 755 | XWindowChanges wc; 756 | 757 | if ((c = wintoclient(ev->window))) { 758 | if (ev->value_mask & CWBorderWidth) 759 | c->bw = ev->border_width; 760 | else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { 761 | m = c->mon; 762 | if (ev->value_mask & CWX) { 763 | c->oldx = c->x; 764 | c->x = m->mx + ev->x; 765 | } 766 | if (ev->value_mask & CWY) { 767 | c->oldy = c->y; 768 | c->y = m->my + ev->y; 769 | } 770 | if (ev->value_mask & CWWidth) { 771 | c->oldw = c->w; 772 | c->w = ev->width; 773 | } 774 | if (ev->value_mask & CWHeight) { 775 | c->oldh = c->h; 776 | c->h = ev->height; 777 | } 778 | if ((c->x + c->w) > m->mx + m->mw && c->isfloating) 779 | c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ 780 | if ((c->y + c->h) > m->my + m->mh && c->isfloating) 781 | c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ 782 | if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) 783 | configure(c); 784 | if (ISVISIBLE(c)) 785 | XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); 786 | } else 787 | configure(c); 788 | } else { 789 | wc.x = ev->x; 790 | wc.y = ev->y; 791 | wc.width = ev->width; 792 | wc.height = ev->height; 793 | wc.border_width = ev->border_width; 794 | wc.sibling = ev->above; 795 | wc.stack_mode = ev->detail; 796 | XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); 797 | } 798 | XSync(dpy, False); 799 | } 800 | 801 | Monitor * 802 | createmon(void) 803 | { 804 | Monitor *m; 805 | 806 | m = ecalloc(1, sizeof(Monitor)); 807 | m->tagset[0] = m->tagset[1] = 1; 808 | m->mfact = mfact; 809 | m->nmaster = nmaster; 810 | m->showbar = showbar; 811 | m->showtitle = showtitle; 812 | m->showtags = showtags; 813 | m->showlayout = showlayout; 814 | m->showstatus = showstatus; 815 | m->showfloating = showfloating; 816 | m->topbar = topbar; 817 | m->gappih = gappih; 818 | m->gappiv = gappiv; 819 | m->gappoh = gappoh; 820 | m->gappov = gappov; 821 | m->lt[0] = &layouts[0]; 822 | m->lt[1] = &layouts[1 % LENGTH(layouts)]; 823 | strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); 824 | return m; 825 | } 826 | 827 | void 828 | destroynotify(XEvent *e) 829 | { 830 | Client *c; 831 | XDestroyWindowEvent *ev = &e->xdestroywindow; 832 | 833 | if ((c = wintoclient(ev->window))) 834 | unmanage(c, 1); 835 | 836 | else if ((c = swallowingclient(ev->window))) 837 | unmanage(c->swallowing, 1); 838 | } 839 | 840 | void 841 | detach(Client *c) 842 | { 843 | Client **tc; 844 | 845 | for (int i = 1; i < LENGTH(tags); i++) 846 | if (c == c->mon->tagmarked[i]) 847 | c->mon->tagmarked[i] = NULL; 848 | 849 | for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); 850 | *tc = c->next; 851 | } 852 | 853 | void 854 | detachstack(Client *c) 855 | { 856 | Client **tc, *t; 857 | 858 | for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); 859 | *tc = c->snext; 860 | 861 | if (c == c->mon->sel) { 862 | for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); 863 | c->mon->sel = t; 864 | } 865 | } 866 | 867 | Monitor * 868 | dirtomon(int dir) 869 | { 870 | Monitor *m = NULL; 871 | 872 | if (dir > 0) { 873 | if (!(m = selmon->next)) 874 | m = mons; 875 | } else if (selmon == mons) 876 | for (m = mons; m->next; m = m->next); 877 | else 878 | for (m = mons; m->next != selmon; m = m->next); 879 | return m; 880 | } 881 | 882 | void 883 | drawbar(Monitor *m) /* take a pointer to the monitor we want to draw the bar on */ 884 | { 885 | int x, w, tw = 0; /* x pos, width, and text width */ 886 | int boxs = drw->fonts->h / 9; /* the little square box for indicators */ 887 | int boxw = drw->fonts->h / 6 + 2; 888 | unsigned int i, occ = 0, urg = 0; /* track which tags are in use and which are urgent */ 889 | Client *c; /* pointer to iterate over list of windows on this monitor */ 890 | 891 | if (!m->showbar) /* if the bar is hidden, do not draw it */ 892 | return; 893 | 894 | /* draw status first so it can be overdrawn by tags later */ 895 | if (m == selmon && selmon->showstatus) { /* status is only drawn on selected monitor */ 896 | char *text, *s, ch; 897 | drw_setscheme(drw, scheme[SchemeStatus]); /* set the colorscheme used by the drawing context */ 898 | x = 0; /* keep track of horiz pos whil drawing */ 899 | for (text = s = stext; *s; s++) { /* this loop handles the dwmblocks clickable blocks */ 900 | if ((unsigned char)(*s) < ' ') { /* ctrl chars (ASCII < 32) are used to separate click events */ 901 | ch = *s; 902 | *s = '\0'; 903 | tw = TEXTW(text) - lrpad; 904 | drw_text(drw, m->ww - statusw + x, 0, tw, bh, 0, text, 0); 905 | x += tw; 906 | *s = ch; 907 | text = s + 1; 908 | } 909 | } 910 | tw = TEXTW(text) - lrpad + 2; /* draw the last remaining segment after the last ctrl char */ 911 | drw_text(drw, m->ww - statusw + x, 0, tw, bh, 0, text, 0); 912 | tw = statusw; 913 | } 914 | 915 | for (c = m->clients; c; c = c->next) { /* drawing tag indicators */ 916 | occ |= c->tags == TAGMASK ? 0 : c->tags; /* if a client has all tags, skip it */ 917 | if (c->isurgent && selmon->showtags) 918 | urg |= c->tags; 919 | } 920 | x = 0; 921 | for (i = 0; i < LENGTH(tags); i++) { /* loop over each tag */ 922 | if(!(occ & 1 << i || m->tagset[m->seltags] & 1 << i)) /* do not draw vacant tags */ 923 | continue; 924 | if (selmon->showtags) { 925 | w = TEXTW(tags[i]); 926 | drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeTagsSel : SchemeTagsNorm]); 927 | drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); 928 | if (occ & 1 << i && selmon->showfloating) /* if tag has a win and showfloating is enabled, draw the floating indicator */ 929 | drw_rect(drw, x + boxs, boxs, boxw, boxw, 930 | m == selmon && selmon->sel && selmon->sel->tags & 1 << i, 931 | urg & 1 << i); 932 | x += w; /* move cursor to the right to prepare for drawing next tag */ 933 | } 934 | } 935 | 936 | /* draw layout indicator if selmon->showlayout */ 937 | if (selmon->showlayout) { 938 | w = TEXTW(m->ltsymbol); 939 | drw_setscheme(drw, scheme[SchemeTagsNorm]); 940 | x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); 941 | } 942 | 943 | 944 | if ((w = m->ww - tw - x) > bh) { 945 | if (m->sel && selmon->showtitle) { 946 | drw_setscheme(drw, scheme[m == selmon ? SchemeInfoSel : SchemeInfoNorm]); 947 | drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); 948 | if (m->sel->isfloating && selmon->showfloating) 949 | drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); 950 | } else { 951 | drw_setscheme(drw, scheme[SchemeInfoNorm]); 952 | drw_rect(drw, x, 0, w, bh, 1, 1); 953 | } 954 | } 955 | drw_map(drw, m->barwin, 0, 0, m->ww, bh); 956 | } 957 | 958 | void 959 | drawbars(void) 960 | { /* loop through every monitor to redraw statusbar */ 961 | Monitor *m; 962 | 963 | for (m = mons; m; m = m->next) 964 | drawbar(m); 965 | } 966 | 967 | void 968 | enternotify(XEvent *e) 969 | { 970 | Client *c; 971 | Monitor *m; 972 | XCrossingEvent *ev = &e->xcrossing; 973 | 974 | if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) 975 | return; 976 | c = wintoclient(ev->window); 977 | m = c ? c->mon : wintomon(ev->window); 978 | if (m != selmon) { 979 | unfocus(selmon->sel, 1); 980 | selmon = m; 981 | } else if (!c || c == selmon->sel) 982 | return; 983 | focus(c); 984 | } 985 | 986 | void 987 | expose(XEvent *e) 988 | { 989 | Monitor *m; 990 | XExposeEvent *ev = &e->xexpose; 991 | 992 | if (ev->count == 0 && (m = wintomon(ev->window))) 993 | drawbar(m); 994 | } 995 | 996 | void 997 | focus(Client *c) 998 | { /* handling when focus shifts to a different client */ 999 | if (!c || !ISVISIBLE(c)) /* if the target client c isn't visible, */ 1000 | for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); /* search the stack for the next visible one to focus */ 1001 | if (selmon->sel && selmon->sel != c) /* if there's a selected window that is not c, */ 1002 | unfocus(selmon->sel, 0); /* unfocus it */ 1003 | if (c) { 1004 | if (c->mon != selmon) /* if the new client c is on a different monitor, */ 1005 | selmon = c->mon; /* switch selmon (selected monitor) to that monitor */ 1006 | if (c->isurgent) /* if urgent state was marked, */ 1007 | seturgent(c, 0); /* clear urgent state */ 1008 | detachstack(c); 1009 | attachstack(c); /* move c to top of the stack */ 1010 | grabbuttons(c, 1); 1011 | XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); 1012 | setfocus(c); /* inform X11 that c now has input focus */ 1013 | } else { 1014 | XSetInputFocus(dpy, selmon->barwin, RevertToPointerRoot, CurrentTime); 1015 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); 1016 | } 1017 | if(selmon->sel && selmon->sel->isfullscreen){ /* if previous client was fullscreen, toggle off and then back for the new */ 1018 | togglefullscreen(NULL); 1019 | selmon->sel = c; 1020 | togglefullscreen(NULL); 1021 | }else{ 1022 | selmon->sel = c; 1023 | } 1024 | drawbars(); /* redraw statusbar */ 1025 | } 1026 | 1027 | /* there are some broken focus acquiring clients needing extra handling */ 1028 | void 1029 | focusin(XEvent *e) 1030 | { /* when a window receives focus */ 1031 | XFocusChangeEvent *ev = &e->xfocus; 1032 | 1033 | if (selmon->sel && ev->window != selmon->sel->win) 1034 | setfocus(selmon->sel); 1035 | } 1036 | 1037 | void 1038 | focusmaster(const Arg *arg) 1039 | { 1040 | Client *master; 1041 | 1042 | if (selmon->nmaster > 1) 1043 | return; 1044 | if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) 1045 | return; 1046 | 1047 | master = nexttiled(selmon->clients); 1048 | 1049 | if (!master) 1050 | return; 1051 | 1052 | int i; 1053 | for (i = 0; !(selmon->tagset[selmon->seltags] & 1 << i); i++); 1054 | i++; 1055 | 1056 | if (selmon->sel == master) { 1057 | if (selmon->tagmarked[i] && ISVISIBLE(selmon->tagmarked[i])) 1058 | focus(selmon->tagmarked[i]); 1059 | } else { 1060 | selmon->tagmarked[i] = selmon->sel; 1061 | focus(master); 1062 | } 1063 | } 1064 | 1065 | void 1066 | focusmon(const Arg *arg) 1067 | { 1068 | Monitor *m; 1069 | 1070 | if (!mons->next) 1071 | return; 1072 | if ((m = dirtomon(arg->i)) == selmon) 1073 | return; 1074 | unfocus(selmon->sel, 0); 1075 | XWarpPointer(dpy, None, m->barwin, 0, 0, 0, 0, m->mw / 2, m->mh / 2); 1076 | selmon = m; 1077 | focus(NULL); 1078 | } 1079 | 1080 | void 1081 | focusstack(const Arg *arg) 1082 | { 1083 | int i = stackpos(arg); 1084 | Client *c, *p; 1085 | 1086 | if(i < 0) 1087 | return; 1088 | for(p = NULL, c = selmon->clients; c && (i || !ISVISIBLE(c)); 1089 | i -= ISVISIBLE(c) ? 1 : 0, p = c, c = c->next); 1090 | focus(c ? c : p); 1091 | restack(selmon); 1092 | } 1093 | 1094 | Atom 1095 | getatomprop(Client *c, Atom prop) 1096 | { 1097 | int di; 1098 | unsigned long dl; 1099 | unsigned char *p = NULL; 1100 | Atom da, atom = None; 1101 | 1102 | if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, 1103 | &da, &di, &dl, &dl, &p) == Success && p) { 1104 | atom = *(Atom *)p; 1105 | XFree(p); 1106 | } 1107 | return atom; 1108 | } 1109 | 1110 | 1111 | pid_t 1112 | getstatusbarpid(void) 1113 | { 1114 | char buf[32], *str = buf, *c; 1115 | FILE *fp; 1116 | 1117 | if (statuspid > 0) { 1118 | snprintf(buf, sizeof(buf), "/proc/%u/cmdline", statuspid); 1119 | if ((fp = fopen(buf, "r"))) { 1120 | fgets(buf, sizeof(buf), fp); 1121 | while ((c = strchr(str, '/'))) 1122 | str = c + 1; 1123 | fclose(fp); 1124 | if (!strcmp(str, STATUSBAR)) 1125 | return statuspid; 1126 | } 1127 | } 1128 | 1129 | #ifdef __FreeBSD__ 1130 | if (!(fp = popen("pgrep "STATUSBAR, "r"))) 1131 | #else 1132 | if (!(fp = popen("pidof -s "STATUSBAR, "r"))) 1133 | #endif 1134 | return -1; 1135 | 1136 | 1137 | fgets(buf, sizeof(buf), fp); 1138 | pclose(fp); 1139 | return strtol(buf, NULL, 10); 1140 | } 1141 | 1142 | 1143 | int 1144 | getrootptr(int *x, int *y) 1145 | { 1146 | int di; 1147 | unsigned int dui; 1148 | Window dummy; 1149 | 1150 | return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); 1151 | } 1152 | 1153 | long 1154 | getstate(Window w) 1155 | { 1156 | int format; 1157 | long result = -1; 1158 | unsigned char *p = NULL; 1159 | unsigned long n, extra; 1160 | Atom real; 1161 | 1162 | if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], 1163 | &real, &format, &n, &extra, (unsigned char **)&p) != Success) 1164 | return -1; 1165 | if (n != 0) 1166 | result = *p; 1167 | XFree(p); 1168 | return result; 1169 | } 1170 | 1171 | int 1172 | gettextprop(Window w, Atom atom, char *text, unsigned int size) 1173 | { 1174 | char **list = NULL; 1175 | int n; 1176 | XTextProperty name; 1177 | 1178 | if (!text || size == 0) 1179 | return 0; 1180 | text[0] = '\0'; 1181 | if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) 1182 | return 0; 1183 | if (name.encoding == XA_STRING) { 1184 | strncpy(text, (char *)name.value, size - 1); 1185 | } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { 1186 | strncpy(text, *list, size - 1); 1187 | XFreeStringList(list); 1188 | } 1189 | text[size - 1] = '\0'; 1190 | XFree(name.value); 1191 | return 1; 1192 | } 1193 | 1194 | void 1195 | grabbuttons(Client *c, int focused) 1196 | { 1197 | updatenumlockmask(); 1198 | { 1199 | unsigned int i, j; 1200 | unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; 1201 | XUngrabButton(dpy, AnyButton, AnyModifier, c->win); 1202 | if (!focused) 1203 | XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, 1204 | BUTTONMASK, GrabModeSync, GrabModeSync, None, None); 1205 | for (i = 0; i < LENGTH(buttons); i++) 1206 | if (buttons[i].click == ClkClientWin) 1207 | for (j = 0; j < LENGTH(modifiers); j++) 1208 | XGrabButton(dpy, buttons[i].button, 1209 | buttons[i].mask | modifiers[j], 1210 | c->win, False, BUTTONMASK, 1211 | GrabModeAsync, GrabModeSync, None, None); 1212 | } 1213 | } 1214 | 1215 | void 1216 | grabkeys(void) 1217 | { 1218 | updatenumlockmask(); 1219 | { 1220 | unsigned int i, j, k; 1221 | unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; 1222 | int start, end, skip; 1223 | KeySym *syms; 1224 | 1225 | XUngrabKey(dpy, AnyKey, AnyModifier, root); 1226 | XDisplayKeycodes(dpy, &start, &end); 1227 | syms = XGetKeyboardMapping(dpy, start, end - start + 1, &skip); 1228 | if (!syms) 1229 | return; 1230 | for (k = start; k <= end; k++) 1231 | for (i = 0; i < LENGTH(keys); i++) 1232 | /* skip modifier codes, we do that ourselves */ 1233 | if (keys[i].keysym == syms[(k - start) * skip]) 1234 | for (j = 0; j < LENGTH(modifiers); j++) 1235 | XGrabKey(dpy, k, 1236 | keys[i].mod | modifiers[j], 1237 | root, True, 1238 | GrabModeAsync, GrabModeAsync); 1239 | XFree(syms); 1240 | } 1241 | } 1242 | 1243 | void 1244 | incnmaster(const Arg *arg) 1245 | { 1246 | selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); 1247 | arrange(selmon); 1248 | } 1249 | 1250 | #ifdef XINERAMA 1251 | static int 1252 | isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) 1253 | { 1254 | while (n--) 1255 | if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org 1256 | && unique[n].width == info->width && unique[n].height == info->height) 1257 | return 0; 1258 | return 1; 1259 | } 1260 | #endif /* XINERAMA */ 1261 | 1262 | void 1263 | keypress(XEvent *e) /* receives a keyboard press XEvent from X11 */ 1264 | { 1265 | unsigned int i; /* will loop over keybinds */ 1266 | KeySym keysym; /* symbolic name, like XK_b or XK_space */ 1267 | XKeyEvent *ev; /* pointer to the key event data */ 1268 | 1269 | ev = &e->xkey; /* grabs the XKeyEvent from XEvent */ 1270 | keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); /* translates raw keycode into symbolic keysym */ 1271 | for (i = 0; i < LENGTH(keys); i++) /* loop thru all binds in keys[] (config.h) */ 1272 | if (keysym == keys[i].keysym 1273 | && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) 1274 | && keys[i].func) /* check that a pointer function exists */ 1275 | keys[i].func(&(keys[i].arg)); /* if the pressed key matches a bind, call the function associated w/ that bind */ 1276 | } 1277 | 1278 | void 1279 | killthis(Client *c) { 1280 | if (!sendevent(c, wmatom[WMDelete])) { 1281 | XGrabServer(dpy); 1282 | XSetErrorHandler(xerrordummy); 1283 | XSetCloseDownMode(dpy, DestroyAll); 1284 | XKillClient(dpy, c->win); 1285 | XSync(dpy, False); 1286 | XSetErrorHandler(xerror); 1287 | XUngrabServer(dpy); 1288 | } 1289 | } 1290 | 1291 | void 1292 | killclient(const Arg *arg) 1293 | { 1294 | Client *c; 1295 | 1296 | if (!selmon->sel) 1297 | return; 1298 | 1299 | if (!arg->ui || arg->ui == 0) { 1300 | killthis(selmon->sel); 1301 | return; 1302 | } 1303 | 1304 | for (c = selmon->clients; c; c = c->next) { 1305 | if (!ISVISIBLE(c) || (arg->ui == 1 && c == selmon->sel)) 1306 | continue; 1307 | killthis(c); 1308 | } 1309 | } 1310 | 1311 | void 1312 | loadxrdb(void) 1313 | { 1314 | Display *display; 1315 | char * resm; 1316 | XrmDatabase xrdb; 1317 | char *type; 1318 | XrmValue value; 1319 | 1320 | display = XOpenDisplay(NULL); 1321 | 1322 | if (display != NULL) { 1323 | resm = XResourceManagerString(display); 1324 | 1325 | if (resm != NULL) { 1326 | xrdb = XrmGetStringDatabase(resm); 1327 | 1328 | if (xrdb != NULL) { 1329 | XRDB_LOAD_COLOR("dwm.normbordercolor", normbordercolor); 1330 | XRDB_LOAD_COLOR("dwm.normbgcolor", normbgcolor); 1331 | XRDB_LOAD_COLOR("dwm.normfgcolor", normfgcolor); 1332 | XRDB_LOAD_COLOR("dwm.selbordercolor", selbordercolor); 1333 | XRDB_LOAD_COLOR("dwm.selbgcolor", selbgcolor); 1334 | XRDB_LOAD_COLOR("dwm.selfgcolor", selfgcolor); 1335 | } 1336 | } 1337 | } 1338 | 1339 | XCloseDisplay(display); 1340 | } 1341 | 1342 | void 1343 | manage(Window w, XWindowAttributes *wa) 1344 | { /* after a window is passed from maprequest, manage determines how to treat it and add it to the stack */ 1345 | Client *c, *t = NULL, *term = NULL; 1346 | Window trans = None; 1347 | XWindowChanges wc; 1348 | 1349 | c = ecalloc(1, sizeof(Client)); /* allocate and initialize a new Client struct to represent the window */ 1350 | c->win = w; 1351 | c->pid = winpid(w); /* pid used for things like swallowing */ 1352 | /* geometry using XWindowAttributes */ 1353 | c->x = c->oldx = wa->x; 1354 | c->y = c->oldy = wa->y; 1355 | c->w = c->oldw = wa->width; 1356 | c->h = c->oldh = wa->height; 1357 | c->oldbw = wa->border_width; 1358 | 1359 | updatetitle(c); /* grabs title */ 1360 | if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { /* if transient, it inherits tags+mon of parent */ 1361 | c->mon = t->mon; 1362 | c->tags = t->tags; 1363 | } else { 1364 | c->mon = selmon; /* assigns to selected monitor */ 1365 | term = termforwin(c); /* tries to find a terminal for swallowing */ 1366 | applyrules(c); /* apply any matching tag rules from config.h */ 1367 | } 1368 | /* ensure window fits to visible bounds of monitor */ 1369 | if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww) 1370 | c->x = c->mon->wx + c->mon->ww - WIDTH(c); 1371 | if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh) 1372 | c->y = c->mon->wy + c->mon->wh - HEIGHT(c); 1373 | c->x = MAX(c->x, c->mon->wx); 1374 | c->y = MAX(c->y, c->mon->wy); 1375 | c->bw = borderpx; 1376 | 1377 | wc.border_width = c->bw; 1378 | XConfigureWindow(dpy, w, CWBorderWidth, &wc); 1379 | XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); 1380 | configure(c); /* propagates border_width, if size doesn't change */ 1381 | updatewindowtype(c); 1382 | updatesizehints(c); 1383 | updatewmhints(c); 1384 | XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); 1385 | grabbuttons(c, 0); 1386 | if (!c->isfloating) 1387 | c->isfloating = c->oldstate = trans != None || c->isfixed; 1388 | if (c->isfloating) 1389 | XRaiseWindow(dpy, c->win); 1390 | attach(c); /* add to the monitor's client list */ 1391 | attachstack(c); /* add it to the stack */ 1392 | XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, /* add to X11 client list */ 1393 | (unsigned char *) &(c->win), 1); 1394 | XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ 1395 | setclientstate(c, NormalState); 1396 | if(selmon->sel && selmon->sel->isfullscreen && !c->isfloating) /* if a fullscreen window was focused, toggle fullscreen */ 1397 | setfullscreen(selmon->sel, 0); 1398 | if (c->mon == selmon) 1399 | unfocus(selmon->sel, 0); /* unfocus other monitor if new window is on current monitor */ 1400 | c->mon->sel = c; 1401 | arrange(c->mon); /* recalc based on layout */ 1402 | XMapWindow(dpy, c->win); 1403 | if (term) 1404 | swallow(term, c); /* if new window is child of a terminal, replace terminal (swallow) */ 1405 | focus(NULL); /* focus the client */ 1406 | } 1407 | 1408 | void 1409 | mappingnotify(XEvent *e) 1410 | { /* keyboard mapping change */ 1411 | XMappingEvent *ev = &e->xmapping; 1412 | 1413 | XRefreshKeyboardMapping(ev); 1414 | if (ev->request == MappingKeyboard) 1415 | grabkeys(); 1416 | } 1417 | 1418 | void 1419 | maprequest(XEvent *e) 1420 | { /* window requests map */ 1421 | static XWindowAttributes wa; /* wa will now hold window attributes */ 1422 | XMapRequestEvent *ev = &e->xmaprequest; 1423 | 1424 | if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect) 1425 | return; /* gets window geom and data */ 1426 | if (!wintoclient(ev->window)) /* if window is not already managed by dwm */ 1427 | manage(ev->window, &wa); /* call manage to start managing it */ 1428 | } 1429 | 1430 | void 1431 | monocle(Monitor *m) 1432 | { 1433 | unsigned int n = 0; 1434 | Client *c; 1435 | 1436 | for (c = m->clients; c; c = c->next) 1437 | if (ISVISIBLE(c)) 1438 | n++; 1439 | if (n > 0) /* override layout symbol */ 1440 | snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); 1441 | for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) 1442 | resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); 1443 | } 1444 | 1445 | void 1446 | motionnotify(XEvent *e) 1447 | { 1448 | static Monitor *mon = NULL; 1449 | Monitor *m; 1450 | XMotionEvent *ev = &e->xmotion; 1451 | 1452 | if (ev->window != root) 1453 | return; 1454 | if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { 1455 | unfocus(selmon->sel, 1); 1456 | selmon = m; 1457 | focus(NULL); 1458 | } 1459 | mon = m; 1460 | } 1461 | 1462 | void 1463 | movemouse(const Arg *arg) 1464 | { 1465 | int x, y, ocx, ocy, nx, ny; 1466 | Client *c; 1467 | Monitor *m; 1468 | XEvent ev; 1469 | Time lasttime = 0; 1470 | 1471 | if (!(c = selmon->sel)) 1472 | return; 1473 | if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ 1474 | return; 1475 | restack(selmon); 1476 | ocx = c->x; 1477 | ocy = c->y; 1478 | if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, 1479 | None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) 1480 | return; 1481 | if (!getrootptr(&x, &y)) 1482 | return; 1483 | do { 1484 | XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); 1485 | switch(ev.type) { 1486 | case ConfigureRequest: 1487 | case Expose: 1488 | case MapRequest: 1489 | handler[ev.type](&ev); 1490 | break; 1491 | case MotionNotify: 1492 | if ((ev.xmotion.time - lasttime) <= (1000 / 60)) 1493 | continue; 1494 | lasttime = ev.xmotion.time; 1495 | 1496 | nx = ocx + (ev.xmotion.x - x); 1497 | ny = ocy + (ev.xmotion.y - y); 1498 | if (abs(selmon->wx - nx) < snap) 1499 | nx = selmon->wx; 1500 | else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) 1501 | nx = selmon->wx + selmon->ww - WIDTH(c); 1502 | if (abs(selmon->wy - ny) < snap) 1503 | ny = selmon->wy; 1504 | else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) 1505 | ny = selmon->wy + selmon->wh - HEIGHT(c); 1506 | if (!c->isfloating && selmon->lt[selmon->sellt]->arrange 1507 | && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) 1508 | togglefloating(NULL); 1509 | if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) 1510 | resize(c, nx, ny, c->w, c->h, 1); 1511 | break; 1512 | } 1513 | } while (ev.type != ButtonRelease); 1514 | XUngrabPointer(dpy, CurrentTime); 1515 | if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { 1516 | sendmon(c, m); 1517 | selmon = m; 1518 | focus(NULL); 1519 | } 1520 | } 1521 | 1522 | Client * 1523 | nexttiled(Client *c) 1524 | { 1525 | for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); 1526 | return c; 1527 | } 1528 | 1529 | void 1530 | pop(Client *c) 1531 | { 1532 | int i; 1533 | for (i = 0; !(selmon->tagset[selmon->seltags] & 1 << i); i++); 1534 | i++; 1535 | 1536 | c->mon->tagmarked[i] = nexttiled(c->mon->clients); 1537 | detach(c); 1538 | attach(c); 1539 | focus(c); 1540 | arrange(c->mon); 1541 | } 1542 | 1543 | void 1544 | propertynotify(XEvent *e) 1545 | { 1546 | Client *c; 1547 | Window trans; 1548 | XPropertyEvent *ev = &e->xproperty; 1549 | 1550 | if ((ev->window == root) && (ev->atom == XA_WM_NAME)) 1551 | updatestatus(); 1552 | else if (ev->state == PropertyDelete) 1553 | return; /* ignore */ 1554 | else if ((c = wintoclient(ev->window))) { 1555 | switch(ev->atom) { 1556 | default: break; 1557 | case XA_WM_TRANSIENT_FOR: 1558 | if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && 1559 | (c->isfloating = (wintoclient(trans)) != NULL)) 1560 | arrange(c->mon); 1561 | break; 1562 | case XA_WM_NORMAL_HINTS: 1563 | c->hintsvalid = 0; 1564 | break; 1565 | case XA_WM_HINTS: 1566 | updatewmhints(c); 1567 | drawbars(); 1568 | break; 1569 | } 1570 | if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { 1571 | updatetitle(c); 1572 | if (c == c->mon->sel && selmon->showtitle) 1573 | drawbar(c->mon); 1574 | } 1575 | if (ev->atom == netatom[NetWMWindowType]) 1576 | updatewindowtype(c); 1577 | } 1578 | } 1579 | 1580 | void 1581 | pushstack(const Arg *arg) { 1582 | int i = stackpos(arg); 1583 | Client *sel = selmon->sel, *c, *p; 1584 | 1585 | if(i < 0) 1586 | return; 1587 | else if(i == 0) { 1588 | detach(sel); 1589 | attach(sel); 1590 | } 1591 | else { 1592 | for(p = NULL, c = selmon->clients; c; p = c, c = c->next) 1593 | if(!(i -= (ISVISIBLE(c) && c != sel))) 1594 | break; 1595 | c = c ? c : p; 1596 | detach(sel); 1597 | sel->next = c->next; 1598 | c->next = sel; 1599 | } 1600 | arrange(selmon); 1601 | } 1602 | 1603 | void 1604 | quit(const Arg *arg) 1605 | { 1606 | if(arg->i) restart = 1; 1607 | running = 0; 1608 | } 1609 | 1610 | Monitor * 1611 | recttomon(int x, int y, int w, int h) 1612 | { 1613 | Monitor *m, *r = selmon; 1614 | int a, area = 0; 1615 | 1616 | for (m = mons; m; m = m->next) 1617 | if ((a = INTERSECT(x, y, w, h, m)) > area) { 1618 | area = a; 1619 | r = m; 1620 | } 1621 | return r; 1622 | } 1623 | 1624 | void 1625 | resize(Client *c, int x, int y, int w, int h, int interact) 1626 | { 1627 | if (applysizehints(c, &x, &y, &w, &h, interact)) 1628 | resizeclient(c, x, y, w, h); 1629 | } 1630 | 1631 | void 1632 | resizeclient(Client *c, int x, int y, int w, int h) 1633 | { 1634 | XWindowChanges wc; 1635 | 1636 | c->oldx = c->x; c->x = wc.x = x; 1637 | c->oldy = c->y; c->y = wc.y = y; 1638 | c->oldw = c->w; c->w = wc.width = w; 1639 | c->oldh = c->h; c->h = wc.height = h; 1640 | wc.border_width = c->bw; 1641 | XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); 1642 | configure(c); 1643 | XSync(dpy, False); 1644 | } 1645 | 1646 | void 1647 | resizemouse(const Arg *arg) 1648 | { 1649 | int ocx, ocy, nw, nh; 1650 | Client *c; 1651 | Monitor *m; 1652 | XEvent ev; 1653 | Time lasttime = 0; 1654 | 1655 | if (!(c = selmon->sel)) 1656 | return; 1657 | if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ 1658 | return; 1659 | restack(selmon); 1660 | ocx = c->x; 1661 | ocy = c->y; 1662 | if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, 1663 | None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) 1664 | return; 1665 | XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); 1666 | do { 1667 | XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); 1668 | switch(ev.type) { 1669 | case ConfigureRequest: 1670 | case Expose: 1671 | case MapRequest: 1672 | handler[ev.type](&ev); 1673 | break; 1674 | case MotionNotify: 1675 | if ((ev.xmotion.time - lasttime) <= (1000 / 60)) 1676 | continue; 1677 | lasttime = ev.xmotion.time; 1678 | 1679 | nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); 1680 | nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); 1681 | if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww 1682 | && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) 1683 | { 1684 | if (!c->isfloating && selmon->lt[selmon->sellt]->arrange 1685 | && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) 1686 | togglefloating(NULL); 1687 | } 1688 | if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) 1689 | resize(c, c->x, c->y, nw, nh, 1); 1690 | break; 1691 | } 1692 | } while (ev.type != ButtonRelease); 1693 | XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); 1694 | XUngrabPointer(dpy, CurrentTime); 1695 | while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); 1696 | if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { 1697 | sendmon(c, m); 1698 | selmon = m; 1699 | focus(NULL); 1700 | } 1701 | } 1702 | 1703 | void 1704 | restack(Monitor *m) 1705 | { 1706 | Client *c; 1707 | XEvent ev; 1708 | XWindowChanges wc; 1709 | 1710 | drawbar(m); 1711 | if (!m->sel) 1712 | return; 1713 | if (m->sel->isfloating || !m->lt[m->sellt]->arrange) 1714 | XRaiseWindow(dpy, m->sel->win); 1715 | if (m->lt[m->sellt]->arrange) { 1716 | wc.stack_mode = Below; 1717 | wc.sibling = m->barwin; 1718 | for (c = m->stack; c; c = c->snext) 1719 | if (!c->isfloating && ISVISIBLE(c)) { 1720 | XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); 1721 | wc.sibling = c->win; 1722 | } 1723 | } 1724 | XSync(dpy, False); 1725 | while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); 1726 | } 1727 | 1728 | void 1729 | run(void) 1730 | { 1731 | XEvent ev; /* store any type of X11 event received - keypress, mouse, etc. */ 1732 | /* main event loop */ 1733 | XSync(dpy, False); /* all pending X11 requests sent to X server */ 1734 | while (running && !XNextEvent(dpy, &ev)) /* keep running while dwm is supposed to be running, and keep handling events from X */ 1735 | if (handler[ev.type]) /* check if there's a handler for the event type */ 1736 | handler[ev.type](&ev); /* call that handler and pass it the event data */ 1737 | } 1738 | 1739 | void 1740 | scan(void) 1741 | { 1742 | unsigned int i, num; 1743 | Window d1, d2, *wins = NULL; 1744 | XWindowAttributes wa; 1745 | 1746 | if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { 1747 | for (i = 0; i < num; i++) { 1748 | if (!XGetWindowAttributes(dpy, wins[i], &wa) 1749 | || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) 1750 | continue; 1751 | if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) 1752 | manage(wins[i], &wa); 1753 | } 1754 | for (i = 0; i < num; i++) { /* now the transients */ 1755 | if (!XGetWindowAttributes(dpy, wins[i], &wa)) 1756 | continue; 1757 | if (XGetTransientForHint(dpy, wins[i], &d1) 1758 | && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) 1759 | manage(wins[i], &wa); 1760 | } 1761 | if (wins) 1762 | XFree(wins); 1763 | } 1764 | } 1765 | 1766 | void 1767 | sendmon(Client *c, Monitor *m) 1768 | { 1769 | if (c->mon == m) 1770 | return; 1771 | unfocus(c, 1); 1772 | detach(c); 1773 | detachstack(c); 1774 | c->mon = m; 1775 | c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ 1776 | attach(c); 1777 | attachstack(c); 1778 | focus(NULL); 1779 | arrange(NULL); 1780 | } 1781 | 1782 | void 1783 | setclientstate(Client *c, long state) 1784 | { 1785 | long data[] = { state, None }; 1786 | 1787 | XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, 1788 | PropModeReplace, (unsigned char *)data, 2); 1789 | } 1790 | 1791 | int 1792 | sendevent(Client *c, Atom proto) 1793 | { 1794 | int n; 1795 | Atom *protocols; 1796 | int exists = 0; 1797 | XEvent ev; 1798 | 1799 | if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { 1800 | while (!exists && n--) 1801 | exists = protocols[n] == proto; 1802 | XFree(protocols); 1803 | } 1804 | if (exists) { 1805 | ev.type = ClientMessage; 1806 | ev.xclient.window = c->win; 1807 | ev.xclient.message_type = wmatom[WMProtocols]; 1808 | ev.xclient.format = 32; 1809 | ev.xclient.data.l[0] = proto; 1810 | ev.xclient.data.l[1] = CurrentTime; 1811 | XSendEvent(dpy, c->win, False, NoEventMask, &ev); 1812 | } 1813 | return exists; 1814 | } 1815 | 1816 | void 1817 | setfocus(Client *c) 1818 | { 1819 | if (!c->neverfocus) { 1820 | XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); 1821 | XChangeProperty(dpy, root, netatom[NetActiveWindow], 1822 | XA_WINDOW, 32, PropModeReplace, 1823 | (unsigned char *) &(c->win), 1); 1824 | } 1825 | sendevent(c, wmatom[WMTakeFocus]); 1826 | } 1827 | 1828 | void 1829 | setfullscreen(Client *c, int fullscreen) 1830 | { 1831 | if (fullscreen && !c->isfullscreen) { 1832 | XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, 1833 | PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); 1834 | c->isfullscreen = 1; 1835 | c->oldstate = c->isfloating; 1836 | c->oldbw = c->bw; 1837 | c->bw = 0; 1838 | c->isfloating = 1; 1839 | resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); 1840 | XRaiseWindow(dpy, c->win); 1841 | } else if (!fullscreen && c->isfullscreen){ 1842 | XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, 1843 | PropModeReplace, (unsigned char*)0, 0); 1844 | c->isfullscreen = 0; 1845 | c->isfloating = c->oldstate; 1846 | c->bw = c->oldbw; 1847 | c->x = c->oldx; 1848 | c->y = c->oldy; 1849 | c->w = c->oldw; 1850 | c->h = c->oldh; 1851 | resizeclient(c, c->x, c->y, c->w, c->h); 1852 | arrange(c->mon); 1853 | } 1854 | } 1855 | 1856 | 1857 | 1858 | void 1859 | setsticky(Client *c, int sticky) 1860 | { 1861 | 1862 | if(sticky && !c->issticky) { 1863 | XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, 1864 | PropModeReplace, (unsigned char *) &netatom[NetWMSticky], 1); 1865 | c->issticky = 1; 1866 | } else if(!sticky && c->issticky){ 1867 | XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, 1868 | PropModeReplace, (unsigned char *)0, 0); 1869 | c->issticky = 0; 1870 | arrange(c->mon); 1871 | } 1872 | } 1873 | 1874 | 1875 | void 1876 | setlayout(const Arg *arg) 1877 | { 1878 | if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) 1879 | selmon->sellt ^= 1; 1880 | if (arg && arg->v) 1881 | selmon->lt[selmon->sellt] = (Layout *)arg->v; 1882 | strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); 1883 | if (selmon->sel) 1884 | arrange(selmon); 1885 | else 1886 | drawbar(selmon); 1887 | } 1888 | 1889 | /* arg > 1.0 will set mfact absolutely */ 1890 | void 1891 | setmfact(const Arg *arg) 1892 | { 1893 | float f; 1894 | 1895 | if (!arg || !selmon->lt[selmon->sellt]->arrange) 1896 | return; 1897 | f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; 1898 | if (f < 0.05 || f > 0.95) 1899 | return; 1900 | selmon->mfact = f; 1901 | arrange(selmon); 1902 | } 1903 | 1904 | void 1905 | setup(void) 1906 | { 1907 | int i; 1908 | XSetWindowAttributes wa; 1909 | Atom utf8string; 1910 | struct sigaction sa; 1911 | 1912 | /* do not transform children into zombies when they terminate */ 1913 | sigemptyset(&sa.sa_mask); 1914 | sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART; 1915 | sa.sa_handler = SIG_IGN; 1916 | sigaction(SIGCHLD, &sa, NULL); 1917 | 1918 | /* clean up any zombies (inherited from .xinitrc etc) immediately */ 1919 | while (waitpid(-1, NULL, WNOHANG) > 0); 1920 | 1921 | signal(SIGHUP, sighup); 1922 | signal(SIGTERM, sigterm); 1923 | 1924 | /* init screen */ 1925 | screen = DefaultScreen(dpy); /* gets the default screen number from the display (dpy) */ 1926 | sw = DisplayWidth(dpy, screen); 1927 | sh = DisplayHeight(dpy, screen); 1928 | root = RootWindow(dpy, screen); /* dwm attaches to the root window so it can recieve global events */ 1929 | drw = drw_create(dpy, screen, root, sw, sh); /* a drawing context for rendering the UI (like the bar) */ 1930 | if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) 1931 | die("no fonts could be loaded."); 1932 | lrpad = drw->fonts->h; /* sets lrpad to the height of the font, for correct spacing in bar */ 1933 | bh = drw->fonts->h + 2; 1934 | updategeom(); /* setting up monitor geometry, if using multiple monitors, creates a linked list of monitor structs */ 1935 | /* init X11 atoms */ 1936 | /* this is asking the X server for an atom (a unique integer) corresponding to each string */ 1937 | /* if it exists already, the atom ID is returned */ 1938 | utf8string = XInternAtom(dpy, "UTF8_STRING", False); 1939 | wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); 1940 | wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); 1941 | wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); 1942 | wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); 1943 | netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); 1944 | netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); 1945 | netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); 1946 | netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); 1947 | netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); 1948 | netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); 1949 | netatom[NetWMSticky] = XInternAtom(dpy, "_NET_WM_STATE_STICKY", False); 1950 | netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); 1951 | netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); 1952 | netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); 1953 | /* init cursors */ 1954 | cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); 1955 | cursor[CurResize] = drw_cur_create(drw, XC_sizing); 1956 | cursor[CurMove] = drw_cur_create(drw, XC_fleur); 1957 | /* init appearance */ 1958 | scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); /* allocates memory to set up colorschemes */ 1959 | for (i = 0; i < LENGTH(colors); i++) 1960 | scheme[i] = drw_scm_create(drw, colors[i], 3); /* each scheme[i] corresponds to a SchemeNorm, SchemeSel, etc */ 1961 | /* init bars */ 1962 | updatebars(); /* creates a bar window for each monitor */ 1963 | updatestatus(); /* renders the status text */ 1964 | /* supporting window for NetWMCheck */ 1965 | wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); /* a dummy window created so applications can detect the WM */ 1966 | /* setting X11 properties so applications know that dwm is the window manager, and supports EWMH hints */ 1967 | XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, 1968 | PropModeReplace, (unsigned char *) &wmcheckwin, 1); 1969 | XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, 1970 | PropModeReplace, (unsigned char *) "dwm", 3); 1971 | XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, 1972 | PropModeReplace, (unsigned char *) &wmcheckwin, 1); 1973 | /* EWMH support per view */ 1974 | XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, 1975 | PropModeReplace, (unsigned char *) netatom, NetLast); 1976 | XDeleteProperty(dpy, root, netatom[NetClientList]); 1977 | /* select events */ 1978 | wa.cursor = cursor[CurNormal]->cursor; 1979 | wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask 1980 | |ButtonPressMask|PointerMotionMask|EnterWindowMask 1981 | |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; /* X11 events that dwm will listen for */ 1982 | XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); /* applies the event mask and cursor to the root window */ 1983 | XSelectInput(dpy, root, wa.event_mask); 1984 | grabkeys(); /* register the keybinds in config.h */ 1985 | focus(NULL); /* focus needs to start cleanly */ 1986 | } 1987 | 1988 | void 1989 | seturgent(Client *c, int urg) 1990 | { 1991 | XWMHints *wmh; 1992 | 1993 | c->isurgent = urg; 1994 | if (!(wmh = XGetWMHints(dpy, c->win))) 1995 | return; 1996 | wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); 1997 | XSetWMHints(dpy, c->win, wmh); 1998 | XFree(wmh); 1999 | } 2000 | 2001 | void 2002 | showhide(Client *c) 2003 | { 2004 | if (!c) 2005 | return; 2006 | if (ISVISIBLE(c)) { 2007 | /* show clients top down */ 2008 | XMoveWindow(dpy, c->win, c->x, c->y); 2009 | if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) 2010 | resize(c, c->x, c->y, c->w, c->h, 0); 2011 | showhide(c->snext); 2012 | } else { 2013 | /* hide clients bottom up */ 2014 | showhide(c->snext); 2015 | XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); 2016 | } 2017 | } 2018 | 2019 | 2020 | void 2021 | sigstatusbar(const Arg *arg) 2022 | { 2023 | union sigval sv; 2024 | 2025 | if (!statussig) 2026 | return; 2027 | sv.sival_int = arg->i; 2028 | if ((statuspid = getstatusbarpid()) <= 0) 2029 | return; 2030 | 2031 | sigqueue(statuspid, SIGRTMIN+statussig, sv); 2032 | } 2033 | 2034 | 2035 | void 2036 | sighup(int unused) 2037 | { 2038 | Arg a = {.i = 1}; 2039 | quit(&a); 2040 | } 2041 | 2042 | void 2043 | sigterm(int unused) 2044 | { 2045 | Arg a = {.i = 0}; 2046 | quit(&a); 2047 | } 2048 | 2049 | void 2050 | spawn(const Arg *arg) 2051 | { /* runs shell commands, ie. launching programs from keybinds */ 2052 | struct sigaction sa; 2053 | 2054 | if (arg->v == dmenucmd) 2055 | dmenumon[0] = '0' + selmon->num; 2056 | if (fork() == 0) { 2057 | if (dpy) 2058 | close(ConnectionNumber(dpy)); 2059 | setsid(); 2060 | 2061 | sigemptyset(&sa.sa_mask); 2062 | sa.sa_flags = 0; 2063 | sa.sa_handler = SIG_DFL; 2064 | sigaction(SIGCHLD, &sa, NULL); 2065 | 2066 | execvp(((char **)arg->v)[0], (char **)arg->v); 2067 | die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]); 2068 | } 2069 | } 2070 | 2071 | int 2072 | stackpos(const Arg *arg) { 2073 | int n, i; 2074 | Client *c, *l; 2075 | 2076 | if(!selmon->clients) 2077 | return -1; 2078 | 2079 | if(arg->i == PREVSEL) { 2080 | for(l = selmon->stack; l && (!ISVISIBLE(l) || l == selmon->sel); l = l->snext); 2081 | if(!l) 2082 | return -1; 2083 | for(i = 0, c = selmon->clients; c != l; i += ISVISIBLE(c) ? 1 : 0, c = c->next); 2084 | return i; 2085 | } 2086 | else if(ISINC(arg->i)) { 2087 | if(!selmon->sel) 2088 | return -1; 2089 | for(i = 0, c = selmon->clients; c != selmon->sel; i += ISVISIBLE(c) ? 1 : 0, c = c->next); 2090 | for(n = i; c; n += ISVISIBLE(c) ? 1 : 0, c = c->next); 2091 | return MOD(i + GETINC(arg->i), n); 2092 | } 2093 | else if(arg->i < 0) { 2094 | for(i = 0, c = selmon->clients; c; i += ISVISIBLE(c) ? 1 : 0, c = c->next); 2095 | return MAX(i + arg->i, 0); 2096 | } 2097 | else 2098 | return arg->i; 2099 | } 2100 | 2101 | void 2102 | tag(const Arg *arg) 2103 | { 2104 | if (selmon->sel && arg->ui & TAGMASK) { 2105 | selmon->sel->tags = arg->ui & TAGMASK; 2106 | focus(NULL); 2107 | arrange(selmon); 2108 | } 2109 | } 2110 | 2111 | void 2112 | spawntag(const Arg *arg) 2113 | { 2114 | if (arg->ui & TAGMASK) { 2115 | for (int i = LENGTH(tags); i >= 0; i--) { 2116 | if (arg->ui & 1<sel || !mons->next) 2128 | return; 2129 | sendmon(selmon->sel, dirtomon(arg->i)); 2130 | } 2131 | 2132 | 2133 | /* layouts no longer needed here because of vanitygaps.c */ 2134 | 2135 | /* 2136 | void 2137 | tile(Monitor *m) 2138 | { 2139 | unsigned int i, n, h, mw, my, ty; 2140 | Client *c; 2141 | 2142 | for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); 2143 | if (n == 0) 2144 | return; 2145 | 2146 | if (n > m->nmaster) 2147 | mw = m->nmaster ? m->ww * m->mfact : 0; 2148 | else 2149 | mw = m->ww; 2150 | for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) 2151 | if (i < m->nmaster) { 2152 | h = (m->wh - my) / (MIN(n, m->nmaster) - i); 2153 | resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); 2154 | if (my + HEIGHT(c) < m->wh) 2155 | my += HEIGHT(c); 2156 | } else { 2157 | h = (m->wh - ty) / (n - i); 2158 | resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); 2159 | if (ty + HEIGHT(c) < m->wh) 2160 | ty += HEIGHT(c); 2161 | } 2162 | } 2163 | */ 2164 | 2165 | /* 2166 | void 2167 | spiral(Monitor *m) 2168 | // spiral layout for use with fullgaps, mostly works 2169 | // every iteration splits remaining space in half, altering direction 2170 | // even splits horiz, odd splits vert 2171 | // final window takes remaining area 2172 | { 2173 | unsigned int i, n; 2174 | int nx, ny, nw, nh; 2175 | Client *c; 2176 | 2177 | for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); // loop all tiled windows 2178 | if (n == 0) // if no tiles then exit 2179 | return; 2180 | 2181 | nx = m->wx + m->gap->gappx; // set geometry while respecting gaps 2182 | ny = m->wy + m->gap->gappx; 2183 | nw = m->ww - 2 * m->gap->gappx; // double for inner gaps 2184 | nh = m->wh - 2 * m->gap->gappx; 2185 | 2186 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { 2187 | if (i == n - 1) { // give the final window all remaining space 2188 | resize(c, nx, ny, nw - 2 * c->bw, nh - 2 * c->bw, 0); // adjust size for client's border 2189 | } else if (i % 2) { // odd indexed windows get top half (vertically) 2190 | resize(c, nx, ny, nw - 2 * c->bw, nh / 2 - m->gap->gappx - 2 * c->bw, 0); // resize to half height minus inner gap 2191 | ny += nh / 2 + m->gap->gappx; // move ny to next available vert space 2192 | nh -= nh / 2 + m->gap->gappx; // reduce nh accordingly 2193 | } else { // now deal with even index --> left half horizontally 2194 | resize(c, nx, ny, nw / 2 - m->gap->gappx - 2 * c->bw, nh - 2 * c->bw, 0); // same logic as w/ the odds 2195 | nx += nw / 2 + m->gap->gappx; 2196 | nw -= nw / 2 + m->gap->gappx; 2197 | } 2198 | } 2199 | } 2200 | */ 2201 | 2202 | void 2203 | togglebar(const Arg *arg) 2204 | { 2205 | selmon->showbar = !selmon->showbar; 2206 | updatebarpos(selmon); 2207 | XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); 2208 | arrange(selmon); 2209 | } 2210 | 2211 | void 2212 | togglebarcolor(const Arg *arg) 2213 | { /* this really needs to be integrated with the other toggle bar stuff */ 2214 | Clr tmp; 2215 | tmp = scheme[SchemeTagsNorm][ColBorder]; 2216 | scheme[SchemeTagsNorm][ColBorder] = scheme[SchemeTagsNorm][ColBg]; 2217 | scheme[SchemeTagsNorm][ColBg] = tmp; 2218 | tmp = scheme[SchemeTagsSel][ColFg]; 2219 | scheme[SchemeTagsSel][ColFg] = scheme[SchemeTagsSel][ColBg]; 2220 | scheme[SchemeTagsSel][ColBg] = tmp; 2221 | tmp = scheme[SchemeSel][ColFg]; 2222 | scheme[SchemeSel][ColFg] = scheme[SchemeSel][ColBg]; 2223 | scheme[SchemeSel][ColBg] = tmp; 2224 | arrange(selmon); 2225 | } 2226 | 2227 | void 2228 | togglebartags(const Arg *arg) 2229 | { 2230 | selmon->showtags = !selmon->showtags; 2231 | arrange(selmon); 2232 | } 2233 | 2234 | void 2235 | togglebartitle(const Arg *arg) 2236 | { 2237 | selmon->showtitle = !selmon->showtitle; 2238 | arrange(selmon); 2239 | } 2240 | 2241 | void 2242 | togglebarlt(const Arg *arg) 2243 | { 2244 | selmon->showlayout = !selmon->showlayout; 2245 | arrange(selmon); 2246 | } 2247 | 2248 | void 2249 | togglebarstatus(const Arg *arg) 2250 | { 2251 | selmon->showstatus = !selmon->showstatus; 2252 | arrange(selmon); 2253 | } 2254 | 2255 | void 2256 | togglebarfloat(const Arg *arg) 2257 | { 2258 | selmon->showfloating = !selmon->showfloating; 2259 | arrange(selmon); 2260 | } 2261 | 2262 | void 2263 | togglefloating(const Arg *arg) 2264 | { 2265 | if (!selmon->sel) 2266 | return; 2267 | if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ 2268 | return; 2269 | selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; 2270 | if (selmon->sel->isfloating) 2271 | resize(selmon->sel, selmon->sel->x, selmon->sel->y, 2272 | selmon->sel->w, selmon->sel->h, 0); 2273 | arrange(selmon); 2274 | } 2275 | 2276 | void 2277 | togglesticky(const Arg *arg) 2278 | { 2279 | if (!selmon->sel) 2280 | return; 2281 | setsticky(selmon->sel, !selmon->sel->issticky); 2282 | arrange(selmon); 2283 | } 2284 | 2285 | void 2286 | togglefullscreen(const Arg *arg) 2287 | { 2288 | if (selmon->sel){ 2289 | setfullscreen(selmon->sel, !selmon->sel->isfullscreen); 2290 | } 2291 | } 2292 | 2293 | void 2294 | toggletag(const Arg *arg) 2295 | { 2296 | unsigned int newtags; 2297 | 2298 | if (!selmon->sel) 2299 | return; 2300 | newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); 2301 | if (newtags) { 2302 | selmon->sel->tags = newtags; 2303 | focus(NULL); 2304 | arrange(selmon); 2305 | } 2306 | } 2307 | 2308 | void 2309 | toggleview(const Arg *arg) 2310 | { 2311 | unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); 2312 | 2313 | if (newtagset) { 2314 | selmon->tagset[selmon->seltags] = newtagset; 2315 | focus(NULL); 2316 | arrange(selmon); 2317 | } 2318 | } 2319 | 2320 | void 2321 | unfocus(Client *c, int setfocus) 2322 | { 2323 | if (!c) 2324 | return; 2325 | grabbuttons(c, 0); 2326 | XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); 2327 | if (setfocus) { 2328 | XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); 2329 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); 2330 | } 2331 | } 2332 | 2333 | void 2334 | unmanage(Client *c, int destroyed) 2335 | { /* removing a window from dwm's control */ 2336 | Monitor *m = c->mon; 2337 | XWindowChanges wc; 2338 | int fullscreen = (selmon->sel == c && selmon->sel->isfullscreen)?1:0; 2339 | 2340 | if (c->swallowing) { /* handle swallowing first */ 2341 | unswallow(c); 2342 | return; 2343 | } 2344 | 2345 | Client *s = swallowingclient(c->win); 2346 | if (s) { /* if a window c is being swallowed, it's reversed */ 2347 | free(s->swallowing); 2348 | s->swallowing = NULL; 2349 | arrange(m); 2350 | focus(NULL); 2351 | return; 2352 | } 2353 | 2354 | detach(c); /* remove c from the monitor's client list */ 2355 | detachstack(c); /* remove c from the stacking order */ 2356 | if (!destroyed) { /* restore X11 properties if not destroyed */ 2357 | wc.border_width = c->oldbw; 2358 | XGrabServer(dpy); /* avoid race conditions */ 2359 | XSetErrorHandler(xerrordummy); 2360 | XSelectInput(dpy, c->win, NoEventMask); 2361 | XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ 2362 | XUngrabButton(dpy, AnyButton, AnyModifier, c->win); 2363 | setclientstate(c, WithdrawnState); 2364 | XSync(dpy, False); 2365 | XSetErrorHandler(xerror); 2366 | XUngrabServer(dpy); 2367 | } 2368 | free(c); /* free memory */ 2369 | 2370 | if (!s) { /* recalcs layout now that c is gone */ 2371 | arrange(m); 2372 | focus(NULL); 2373 | if(fullscreen){ /* if fullscreen, toggle it off */ 2374 | togglefullscreen(NULL); 2375 | } 2376 | updateclientlist(); /* update EWMH property */ 2377 | } 2378 | } 2379 | 2380 | void 2381 | unmapnotify(XEvent *e) 2382 | { 2383 | Client *c; 2384 | XUnmapEvent *ev = &e->xunmap; 2385 | 2386 | if ((c = wintoclient(ev->window))) { 2387 | if (ev->send_event) 2388 | setclientstate(c, WithdrawnState); 2389 | else 2390 | unmanage(c, 0); 2391 | } 2392 | } 2393 | 2394 | void 2395 | updatebars(void) 2396 | { 2397 | Monitor *m; 2398 | XSetWindowAttributes wa = { 2399 | .override_redirect = True, 2400 | .background_pixmap = ParentRelative, 2401 | .event_mask = ButtonPressMask|ExposureMask 2402 | }; 2403 | XClassHint ch = {"dwm", "dwm"}; 2404 | for (m = mons; m; m = m->next) { 2405 | if (m->barwin) 2406 | continue; 2407 | m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), 2408 | CopyFromParent, DefaultVisual(dpy, screen), 2409 | CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); 2410 | XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); 2411 | XMapRaised(dpy, m->barwin); 2412 | XSetClassHint(dpy, m->barwin, &ch); 2413 | } 2414 | } 2415 | 2416 | void 2417 | updatebarpos(Monitor *m) 2418 | { 2419 | m->wy = m->my; 2420 | m->wh = m->mh; 2421 | if (m->showbar) { 2422 | m->wh -= bh; 2423 | m->by = m->topbar ? m->wy : m->wy + m->wh; 2424 | m->wy = m->topbar ? m->wy + bh : m->wy; 2425 | } else 2426 | m->by = -bh; 2427 | } 2428 | 2429 | void 2430 | updateclientlist(void) 2431 | { 2432 | Client *c; 2433 | Monitor *m; 2434 | 2435 | XDeleteProperty(dpy, root, netatom[NetClientList]); 2436 | for (m = mons; m; m = m->next) 2437 | for (c = m->clients; c; c = c->next) 2438 | XChangeProperty(dpy, root, netatom[NetClientList], 2439 | XA_WINDOW, 32, PropModeAppend, 2440 | (unsigned char *) &(c->win), 1); 2441 | } 2442 | 2443 | int 2444 | updategeom(void) 2445 | { 2446 | int dirty = 0; 2447 | 2448 | #ifdef XINERAMA 2449 | if (XineramaIsActive(dpy)) { 2450 | int i, j, n, nn; 2451 | Client *c; 2452 | Monitor *m; 2453 | XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn); 2454 | XineramaScreenInfo *unique = NULL; 2455 | 2456 | for (n = 0, m = mons; m; m = m->next, n++); 2457 | /* only consider unique geometries as separate screens */ 2458 | unique = ecalloc(nn, sizeof(XineramaScreenInfo)); 2459 | for (i = 0, j = 0; i < nn; i++) 2460 | if (isuniquegeom(unique, j, &info[i])) 2461 | memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); 2462 | XFree(info); 2463 | nn = j; 2464 | 2465 | /* new monitors if nn > n */ 2466 | for (i = n; i < nn; i++) { 2467 | for (m = mons; m && m->next; m = m->next); 2468 | if (m) 2469 | m->next = createmon(); 2470 | else 2471 | mons = createmon(); 2472 | } 2473 | for (i = 0, m = mons; i < nn && m; m = m->next, i++) 2474 | if (i >= n 2475 | || unique[i].x_org != m->mx || unique[i].y_org != m->my 2476 | || unique[i].width != m->mw || unique[i].height != m->mh) 2477 | { 2478 | dirty = 1; 2479 | m->num = i; 2480 | m->mx = m->wx = unique[i].x_org; 2481 | m->my = m->wy = unique[i].y_org; 2482 | m->mw = m->ww = unique[i].width; 2483 | m->mh = m->wh = unique[i].height; 2484 | updatebarpos(m); 2485 | } 2486 | /* removed monitors if n > nn */ 2487 | for (i = nn; i < n; i++) { 2488 | for (m = mons; m && m->next; m = m->next); 2489 | while ((c = m->clients)) { 2490 | dirty = 1; 2491 | m->clients = c->next; 2492 | detachstack(c); 2493 | c->mon = mons; 2494 | attach(c); 2495 | attachstack(c); 2496 | } 2497 | if (m == selmon) 2498 | selmon = mons; 2499 | cleanupmon(m); 2500 | } 2501 | free(unique); 2502 | } else 2503 | #endif /* XINERAMA */ 2504 | { /* default monitor setup */ 2505 | if (!mons) 2506 | mons = createmon(); 2507 | if (mons->mw != sw || mons->mh != sh) { 2508 | dirty = 1; 2509 | mons->mw = mons->ww = sw; 2510 | mons->mh = mons->wh = sh; 2511 | updatebarpos(mons); 2512 | } 2513 | } 2514 | if (dirty) { 2515 | selmon = mons; 2516 | selmon = wintomon(root); 2517 | } 2518 | return dirty; 2519 | } 2520 | 2521 | void 2522 | updatenumlockmask(void) 2523 | { 2524 | unsigned int i, j; 2525 | XModifierKeymap *modmap; 2526 | 2527 | numlockmask = 0; 2528 | modmap = XGetModifierMapping(dpy); 2529 | for (i = 0; i < 8; i++) 2530 | for (j = 0; j < modmap->max_keypermod; j++) 2531 | if (modmap->modifiermap[i * modmap->max_keypermod + j] 2532 | == XKeysymToKeycode(dpy, XK_Num_Lock)) 2533 | numlockmask = (1 << i); 2534 | XFreeModifiermap(modmap); 2535 | } 2536 | 2537 | void 2538 | updatesizehints(Client *c) 2539 | { 2540 | long msize; 2541 | XSizeHints size; 2542 | 2543 | if (!XGetWMNormalHints(dpy, c->win, &size, &msize)) 2544 | /* size is uninitialized, ensure that size.flags aren't used */ 2545 | size.flags = PSize; 2546 | if (size.flags & PBaseSize) { 2547 | c->basew = size.base_width; 2548 | c->baseh = size.base_height; 2549 | } else if (size.flags & PMinSize) { 2550 | c->basew = size.min_width; 2551 | c->baseh = size.min_height; 2552 | } else 2553 | c->basew = c->baseh = 0; 2554 | if (size.flags & PResizeInc) { 2555 | c->incw = size.width_inc; 2556 | c->inch = size.height_inc; 2557 | } else 2558 | c->incw = c->inch = 0; 2559 | if (size.flags & PMaxSize) { 2560 | c->maxw = size.max_width; 2561 | c->maxh = size.max_height; 2562 | } else 2563 | c->maxw = c->maxh = 0; 2564 | if (size.flags & PMinSize) { 2565 | c->minw = size.min_width; 2566 | c->minh = size.min_height; 2567 | } else if (size.flags & PBaseSize) { 2568 | c->minw = size.base_width; 2569 | c->minh = size.base_height; 2570 | } else 2571 | c->minw = c->minh = 0; 2572 | if (size.flags & PAspect) { 2573 | c->mina = (float)size.min_aspect.y / size.min_aspect.x; 2574 | c->maxa = (float)size.max_aspect.x / size.max_aspect.y; 2575 | } else 2576 | c->maxa = c->mina = 0.0; 2577 | c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); 2578 | c->hintsvalid = 1; 2579 | } 2580 | 2581 | void 2582 | updatestatus(void) 2583 | { 2584 | if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)) && selmon->showstatus) { 2585 | strcpy(stext, "dwm-"VERSION); 2586 | statusw = TEXTW(stext) - lrpad + 2; 2587 | } else { 2588 | char *text, *s, ch; 2589 | 2590 | statusw = 0; 2591 | for (text = s = stext; *s; s++) { 2592 | if ((unsigned char)(*s) < ' ') { 2593 | ch = *s; 2594 | *s = '\0'; 2595 | statusw += TEXTW(text) - lrpad; 2596 | *s = ch; 2597 | text = s + 1; 2598 | } 2599 | } 2600 | statusw += TEXTW(text) - lrpad + 2; 2601 | 2602 | } 2603 | drawbar(selmon); 2604 | } 2605 | 2606 | void 2607 | updatetitle(Client *c) 2608 | { 2609 | if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) 2610 | gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); 2611 | if (c->name[0] == '\0') /* hack to mark broken clients */ 2612 | strcpy(c->name, broken); 2613 | } 2614 | 2615 | void 2616 | updatewindowtype(Client *c) 2617 | { 2618 | Atom state = getatomprop(c, netatom[NetWMState]); 2619 | Atom wtype = getatomprop(c, netatom[NetWMWindowType]); 2620 | 2621 | if (state == netatom[NetWMFullscreen]) 2622 | setfullscreen(c, 1); 2623 | if (state == netatom[NetWMSticky]) { 2624 | setsticky(c, 1); 2625 | } 2626 | if (wtype == netatom[NetWMWindowTypeDialog]) 2627 | c->isfloating = 1; 2628 | } 2629 | 2630 | void 2631 | updatewmhints(Client *c) 2632 | { 2633 | XWMHints *wmh; 2634 | 2635 | if ((wmh = XGetWMHints(dpy, c->win))) { 2636 | if (c == selmon->sel && wmh->flags & XUrgencyHint) { 2637 | wmh->flags &= ~XUrgencyHint; 2638 | XSetWMHints(dpy, c->win, wmh); 2639 | } else 2640 | c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; 2641 | if (wmh->flags & InputHint) 2642 | c->neverfocus = !wmh->input; 2643 | else 2644 | c->neverfocus = 0; 2645 | XFree(wmh); 2646 | } 2647 | } 2648 | 2649 | void 2650 | view(const Arg *arg) 2651 | { 2652 | if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) 2653 | return; 2654 | selmon->seltags ^= 1; /* toggle sel tagset */ 2655 | if (arg->ui & TAGMASK) 2656 | selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; 2657 | focus(NULL); 2658 | arrange(selmon); 2659 | } 2660 | 2661 | pid_t 2662 | winpid(Window w) 2663 | { 2664 | 2665 | pid_t result = 0; 2666 | 2667 | #ifdef __linux__ 2668 | xcb_res_client_id_spec_t spec = {0}; 2669 | spec.client = w; 2670 | spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID; 2671 | 2672 | xcb_generic_error_t *e = NULL; 2673 | xcb_res_query_client_ids_cookie_t c = xcb_res_query_client_ids(xcon, 1, &spec); 2674 | xcb_res_query_client_ids_reply_t *r = xcb_res_query_client_ids_reply(xcon, c, &e); 2675 | 2676 | if (!r) 2677 | return (pid_t)0; 2678 | 2679 | xcb_res_client_id_value_iterator_t i = xcb_res_query_client_ids_ids_iterator(r); 2680 | for (; i.rem; xcb_res_client_id_value_next(&i)) { 2681 | spec = i.data->spec; 2682 | if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) { 2683 | uint32_t *t = xcb_res_client_id_value_value(i.data); 2684 | result = *t; 2685 | break; 2686 | } 2687 | } 2688 | 2689 | free(r); 2690 | 2691 | if (result == (pid_t)-1) 2692 | result = 0; 2693 | 2694 | #endif /* __linux__ */ 2695 | 2696 | #if defined(__OpenBSD__) || defined(__FreeBSD__) 2697 | Atom type; 2698 | int format; 2699 | unsigned long len, bytes; 2700 | unsigned char *prop; 2701 | pid_t ret; 2702 | 2703 | if (XGetWindowProperty(dpy, w, XInternAtom(dpy, "_NET_WM_PID", 0), 0, 1, False, AnyPropertyType, &type, &format, &len, &bytes, &prop) != Success || !prop) 2704 | return 0; 2705 | 2706 | ret = *(pid_t*)prop; 2707 | XFree(prop); 2708 | result = ret; 2709 | 2710 | #endif /* __OpenBSD__ */ 2711 | return result; 2712 | } 2713 | 2714 | pid_t 2715 | getparentprocess(pid_t p) 2716 | { 2717 | unsigned int v = 0; 2718 | 2719 | #ifdef __linux__ 2720 | FILE *f; 2721 | char buf[256]; 2722 | snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p); 2723 | 2724 | if (!(f = fopen(buf, "r"))) 2725 | return 0; 2726 | 2727 | fscanf(f, "%*u %*s %*c %u", &v); 2728 | fclose(f); 2729 | #endif /* __linux__*/ 2730 | 2731 | #ifdef __FreeBSD__ 2732 | FILE *f; 2733 | char buf[256]; 2734 | snprintf(buf, sizeof(buf) - 1, "/proc/%u/status", (unsigned)p); 2735 | 2736 | if (!(f = fopen(buf, "r"))) 2737 | return 0; 2738 | 2739 | fscanf(f, "%*s %*u %u", &v); 2740 | fclose(f); 2741 | #endif 2742 | 2743 | #ifdef __OpenBSD__ 2744 | int n; 2745 | kvm_t *kd; 2746 | struct kinfo_proc *kp; 2747 | 2748 | kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL); 2749 | if (!kd) 2750 | return 0; 2751 | 2752 | kp = kvm_getprocs(kd, KERN_PROC_PID, p, sizeof(*kp), &n); 2753 | v = kp->p_ppid; 2754 | #endif /* __OpenBSD__ */ 2755 | 2756 | return (pid_t)v; 2757 | } 2758 | 2759 | int 2760 | isdescprocess(pid_t p, pid_t c) 2761 | { 2762 | while (p != c && c != 0) 2763 | c = getparentprocess(c); 2764 | 2765 | return (int)c; 2766 | } 2767 | 2768 | Client * 2769 | termforwin(const Client *w) 2770 | { 2771 | Client *c; 2772 | Monitor *m; 2773 | 2774 | if (!w->pid || w->isterminal) 2775 | return NULL; 2776 | 2777 | for (m = mons; m; m = m->next) { 2778 | for (c = m->clients; c; c = c->next) { 2779 | if (c->isterminal && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid)) 2780 | return c; 2781 | } 2782 | } 2783 | 2784 | return NULL; 2785 | } 2786 | 2787 | Client * 2788 | swallowingclient(Window w) 2789 | { 2790 | Client *c; 2791 | Monitor *m; 2792 | 2793 | for (m = mons; m; m = m->next) { 2794 | for (c = m->clients; c; c = c->next) { 2795 | if (c->swallowing && c->swallowing->win == w) 2796 | return c; 2797 | } 2798 | } 2799 | 2800 | return NULL; 2801 | } 2802 | 2803 | Client * 2804 | wintoclient(Window w) 2805 | { 2806 | Client *c; 2807 | Monitor *m; 2808 | 2809 | for (m = mons; m; m = m->next) 2810 | for (c = m->clients; c; c = c->next) 2811 | if (c->win == w) 2812 | return c; 2813 | return NULL; 2814 | } 2815 | 2816 | Monitor * 2817 | wintomon(Window w) 2818 | { /* move window w to monitor m */ 2819 | int x, y; 2820 | Client *c; 2821 | Monitor *m; 2822 | 2823 | if (w == root && getrootptr(&x, &y)) 2824 | return recttomon(x, y, 1, 1); 2825 | for (m = mons; m; m = m->next) 2826 | if (w == m->barwin) 2827 | return m; 2828 | if ((c = wintoclient(w))) 2829 | return c->mon; 2830 | return selmon; 2831 | } 2832 | 2833 | /* There's no way to check accesses to destroyed windows, thus those cases are 2834 | * ignored (especially on UnmapNotify's). Other types of errors call Xlibs 2835 | * default error handler, which may call exit. */ 2836 | int 2837 | xerror(Display *dpy, XErrorEvent *ee) 2838 | { 2839 | if (ee->error_code == BadWindow 2840 | || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) 2841 | || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) 2842 | || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) 2843 | || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) 2844 | || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) 2845 | || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) 2846 | || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) 2847 | || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) 2848 | return 0; 2849 | fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", 2850 | ee->request_code, ee->error_code); 2851 | return xerrorxlib(dpy, ee); /* may call exit */ 2852 | } 2853 | 2854 | int 2855 | xerrordummy(Display *dpy, XErrorEvent *ee) 2856 | { 2857 | return 0; 2858 | } 2859 | 2860 | /* Startup Error handler to check if another window manager 2861 | * is already running. */ 2862 | int 2863 | xerrorstart(Display *dpy, XErrorEvent *ee) 2864 | { 2865 | die("dwm: another window manager is already running"); 2866 | return -1; 2867 | } 2868 | 2869 | void 2870 | xrdb(const Arg *arg) 2871 | { /* load xresource database colors */ 2872 | loadxrdb(); 2873 | int i; 2874 | for (i = 0; i < LENGTH(colors); i++) 2875 | scheme[i] = drw_scm_create(drw, colors[i], 3); 2876 | focus(NULL); 2877 | arrange(NULL); 2878 | } 2879 | 2880 | void 2881 | zoom(const Arg *arg) 2882 | { 2883 | Client *c = selmon->sel; 2884 | 2885 | if (!selmon->lt[selmon->sellt]->arrange || !c || c->isfloating) 2886 | return; 2887 | if (c == nexttiled(selmon->clients) && !(c = nexttiled(c->next))) 2888 | return; 2889 | pop(c); 2890 | } 2891 | 2892 | 2893 | 2894 | int 2895 | main(int argc, char *argv[]) 2896 | { 2897 | if (argc == 2 && !strcmp("-v", argv[1])) 2898 | die("dwm-"VERSION); 2899 | else if (argc != 1) 2900 | die("usage: dwm [-v]"); 2901 | if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) 2902 | fputs("warning: no locale support\n", stderr); 2903 | if (!(dpy = XOpenDisplay(NULL))) 2904 | die("dwm: cannot open display"); 2905 | if (!(xcon = XGetXCBConnection(dpy))) 2906 | die("dwm: cannot get xcb connection\n"); 2907 | checkotherwm(); /* see if any other WMs are running */ 2908 | XrmInitialize(); 2909 | loadxrdb(); /* added by xrdb patch */ 2910 | setup(); /* initialize everything needed to start dwm */ 2911 | #ifdef __OpenBSD__ 2912 | if (pledge("stdio rpath proc exec ps", NULL) == -1) 2913 | die("pledge"); 2914 | #endif /* __OpenBSD__ */ 2915 | scan(); /* see if other applications are already running */ 2916 | run(); /* main event loop of dwm --> 2917 | * continuously listens to events from the X server (window changes, key presses, mouse) 2918 | * and sends them to the correct event handler */ 2919 | if(restart) execvp(argv[0], argv); 2920 | cleanup(); 2921 | XCloseDisplay(dpy); 2922 | return EXIT_SUCCESS; 2923 | } 2924 | --------------------------------------------------------------------------------