├── .gitignore ├── BUGS ├── LICENSE ├── Makefile ├── README.org ├── TODO ├── assets ├── gruvbox.png ├── nord.png ├── solarized_dark.png └── solarized_light.png ├── bin ├── start.sh └── status.sh ├── config.h ├── config.mk ├── drw.c ├── drw.h ├── dwm.1 ├── dwm.c ├── dwm.desktop ├── gaplessgrid.c ├── patches ├── 01_XKeycodeFix.patch ├── 02_monoclecount.patch ├── 03_notitle.patch ├── 04_attachaside.patch ├── 05_occupiedcol.patch ├── 06_uselessgaps.patch ├── 07_runorraise.patch ├── 08_betterborders.patch ├── 09_selfrestart.patch ├── 10_stacker.patch ├── 11_pertag2.patch ├── 12_statuscolors.patch ├── 13_systray.patch └── 14_fifo.patch ├── transient.c ├── util.c └── util.h /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | config.* 3 | *.o 4 | *.orig 5 | *.rej 6 | dwm 7 | .ycm_extra_conf.py 8 | src/ 9 | pkg/ 10 | -------------------------------------------------------------------------------- /BUGS: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | 18:17 < Biolunar> when i change my resolution in dwm (to a smaller one) and then back to the native, the top bar is not repainted. that's since 5.7.2, in 5.6 it worked fine 4 | 18:19 < Biolunar> is it just happening to me or a (known) bug? 5 | 18:24 < Biolunar> and in addition, mplayers fullscreen is limited to the small resolution after i changed it back to the native 6 | 7 | reproducible with xrandr -s but not with --output and --mode, strange 8 | 9 | --- 10 | 11 | yet another corner case: 12 | open a terminal, focus another monitor, but without moving the mouse 13 | pointer there 14 | if there is no client on the other monitor to get the focus, then the 15 | terminal will be unfocused but it will accept input 16 | 17 | --- 18 | 19 | Donald Allen reported this: 20 | 21 | starting emacs from dmenu in archlinux results in missing configure of emacs, but mod1-space or mod1-shift-space fix this problem. this problem is new and did not happen in 1.6 xorg servers 22 | 23 | --- 24 | 25 | voltaic reports this: 26 | 27 | When I use two monitors, one larger in resolution than the other, the 28 | bar is drawn using the smaller x-dimension on both screens. I think 29 | what's happening is that there are two bars drawn, but the short bar 30 | is always on top of the long bar such that I can't see the information 31 | under the short bar. If I switch to the small screen, hide the short 32 | bar, and then switch to the large screen, the long bar is drawn 33 | correctly. 34 | 35 | A similar problem occurs when I have started dwm on a small resolution 36 | monitor (laptop screen) and then I switch to a large external display. 37 | When I do this, the bar itself is drawn for the original smaller 38 | resolution, but the information to be printed on the bar is 39 | right-aligned for a longer bar. So what I see is a bar that has the 40 | right hand side of it cut-off. See attached screenshot. 41 | 42 | I am using standard options for xrandr such as --output VGA1 --auto, etc. 43 | 44 | --- 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT/X Consortium License 2 | 3 | © 2006-2014 Anselm R Garbe 4 | © 2010-2014 Hiltjo Posthuma 5 | © 2007-2011 Peter Hartlich 6 | © 2010-2011 Connor Lane Smith 7 | © 2006-2009 Jukka Salmi 8 | © 2007-2009 Premysl Hruby 9 | © 2007-2009 Szabolcs Nagy 10 | © 2007-2009 Christof Musik 11 | © 2009 Mate Nagy 12 | © 2007-2008 Enno Gottox Boland 13 | © 2008 Martin Hurton 14 | © 2008 Neale Pickett 15 | © 2006-2007 Sander van Dijk 16 | 17 | Permission is hereby granted, free of charge, to any person obtaining a 18 | copy of this software and associated documentation files (the "Software"), 19 | to deal in the Software without restriction, including without limitation 20 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 21 | and/or sell copies of the Software, and to permit persons to whom the 22 | Software is furnished to do so, subject to the following conditions: 23 | 24 | The above copyright notice and this permission notice shall be included in 25 | all copies or substantial portions of the Software. 26 | 27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 30 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 32 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 33 | DEALINGS IN THE SOFTWARE. 34 | -------------------------------------------------------------------------------- /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: options dwm 10 | 11 | options: 12 | @echo dwm build options: 13 | @echo "CFLAGS = ${CFLAGS}" 14 | @echo "LDFLAGS = ${LDFLAGS}" 15 | @echo "CC = ${CC}" 16 | 17 | .c.o: 18 | @echo CC $< 19 | @${CC} -c ${CFLAGS} $< 20 | 21 | ${OBJ}: config.h config.mk 22 | 23 | config.h: 24 | @echo creating $@ from config.def.h 25 | @cp config.def.h $@ 26 | 27 | dwm: ${OBJ} 28 | @echo CC -o $@ 29 | @${CC} -o $@ ${OBJ} ${LDFLAGS} 30 | 31 | clean: 32 | @echo cleaning 33 | @rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz 34 | 35 | dist: clean 36 | @echo creating dist tarball 37 | @mkdir -p dwm-${VERSION} 38 | @cp -R LICENSE Makefile README config.def.h config.mk \ 39 | dwm.1 ${SRC} dwm-${VERSION} 40 | @tar -cf dwm-${VERSION}.tar dwm-${VERSION} 41 | @gzip dwm-${VERSION}.tar 42 | @rm -rf dwm-${VERSION} 43 | 44 | install: all 45 | @echo installing executable file to ${DESTDIR}${PREFIX}/bin 46 | @mkdir -p ${DESTDIR}${PREFIX}/bin 47 | @cp -f dwm ${DESTDIR}${PREFIX}/bin 48 | @chmod 755 ${DESTDIR}${PREFIX}/bin/dwm 49 | @echo installing manual page to ${DESTDIR}${MANPREFIX}/man1 50 | @mkdir -p ${DESTDIR}${MANPREFIX}/man1 51 | @sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1 52 | @chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1 53 | @echo adding .desktop file to /usr/share/xsessions 54 | @mkdir -p /usr/share/xsessions 55 | @cp -f dwm.desktop /usr/share/xsessions 56 | @chmod 644 /usr/share/xsessions/dwm.desktop 57 | 58 | uninstall: 59 | @echo removing executable file from ${DESTDIR}${PREFIX}/bin 60 | @rm -f ${DESTDIR}${PREFIX}/bin/dwm 61 | @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 62 | @rm -f ${DESTDIR}${MANPREFIX}/man1/dwm.1 63 | 64 | .PHONY: all options clean dist install uninstall 65 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | * DWM - Dynamic Window Manager 2 | dwm is an extremely fast, small and dynamic window manager for X. 3 | 4 | This repository exists as my fork of [[https://git.suckless.org/dwm][dwm]] (currently just 6.1), and as such has references and links to my own config. 5 | 6 | *CAVEAT EMPTOR* 7 | 8 | ** Requirements 9 | 10 | I mostly use [[https://archlinux.org][Arch Linux]], as such package names/file paths will be tied to Arch's. If you wish to fork this, it should be quite simple to get working. 11 | 12 | ** Dependencies 13 | 14 | The following packages are needed to build: 15 | 16 | - =libx11= 17 | - =libxft= 18 | - =libxinerama= 19 | 20 | A full running version also depends on the following packages: 21 | 22 | - =xorg-xsetroot= (for status and dwm needs it) 23 | - =xorg-setxkbmap= (for startup) 24 | - =xorg-xset= (for startup) 25 | - =nitrogen= (setting wallpaper) 26 | - =network-manager-applet= (network management) 27 | - =dunst= (notification daemon) 28 | - =picom= (compositor) 29 | - =gnome-screensaver= (for screen lock) 30 | 31 | And for the status: 32 | 33 | - =sysstat= 34 | - =acpi= 35 | 36 | ** Installation 37 | You /might/ have to modify =config.mk= to match any local changes (although I haven't had to). This is also /currently/ where the theme flags are specified. 38 | 39 | After making any changes, run =sudo make install clean= to install dwm and create a =dwm.desktop= file for working with login managers. 40 | 41 | ** Running DWM 42 | 43 | This dwm setup depends on a number of other utilities which get run from the [[file:bin/start.sh][Startup]] and [[file:bin/status.sh][Status]] scripts. 44 | 45 | It also assumes it's being run through a login manager, but passing the [[file:bin/start.sh][Startup]] script to =xinit= should work. 46 | 47 | ** Customisation 48 | 49 | For simplicity, my =dwm.c= is already patched with everything in =patches/= but all the ordered patches should apply from a base 6.1. 50 | 51 | There also exists a number of themes (shown below) 52 | 53 | | [[assets/nord.png]] | [[assets/gruvbox.png]] | [[assets/solarized_dark.png]] | [[assets/solarized_light.png]] | 54 | | *Nord* (-DNORD) | *Gruvbox* (-DGRUVBOX) | *Solarized Dark* (-DSOLARIZED_DARK) | *Solarized Light* (-DSOLARIZED_LIGHT) | 55 | 56 | Which can be displayed with the associated flag in =config.mk=. All included themes also styles clients appropriately, as well as dmenu. 57 | 58 | *** Adding a theme 59 | 60 | If you wish to contribute your own theme, add an appropriate =#IFDEF THEMENAME= block in [[file:config.h][config.h]] to specify the colours, and further down for dmenu. 61 | 62 | You also need to adjust the block in =dwm.c= within =updatesystray= to specify the background. 63 | 64 | ** Status 65 | 66 | The status area is a simple script with a number of functions, currently just CPU, RAM, time, speaker status and battery (unless full). 67 | 68 | It relies on =xsetroot= to echo a large formatted string. I accept that this isn't the best way to write this /however/ programmers are lazy. 69 | 70 | ** Screen Locking 71 | 72 | I use =gdm= as my login manager because it's simple, clean and it just works. 73 | 74 | Along with the below config, closing the lid locks and suspends the computer. 75 | 76 | */etc/systemd/logind.conf*: 77 | #+name: /etc/systemd/logind.conf 78 | #+begin_src conf 79 | # This file is part of systemd. 80 | # 81 | # systemd is free software; you can redistribute it and/or modify it 82 | # under the terms of the GNU Lesser General Public License as published by 83 | # the Free Software Foundation; either version 2.1 of the License, or 84 | # (at your option) any later version. 85 | # 86 | # Entries in this file show the compile time defaults. 87 | # You can change settings by editing this file. 88 | # Defaults can be restored by simply deleting this file. 89 | # 90 | # See logind.conf(5) for details. 91 | 92 | [Login] 93 | HandlePowerKey=poweroff 94 | HandleSuspendKey=suspend 95 | HandleHibernateKey=hibernate 96 | HandleLidSwitch=suspend 97 | HandleLidSwitchExternalPower=suspend 98 | HandleLidSwitchDocked=ignore 99 | 100 | #+end_src 101 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - Figure out why tagspacing doesn't work 2 | -------------------------------------------------------------------------------- /assets/gruvbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elken/dwm/3e0e1ca749782274dd4c86ceb21b53338e56e1a5/assets/gruvbox.png -------------------------------------------------------------------------------- /assets/nord.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elken/dwm/3e0e1ca749782274dd4c86ceb21b53338e56e1a5/assets/nord.png -------------------------------------------------------------------------------- /assets/solarized_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elken/dwm/3e0e1ca749782274dd4c86ceb21b53338e56e1a5/assets/solarized_dark.png -------------------------------------------------------------------------------- /assets/solarized_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elken/dwm/3e0e1ca749782274dd4c86ceb21b53338e56e1a5/assets/solarized_light.png -------------------------------------------------------------------------------- /bin/start.sh: -------------------------------------------------------------------------------- 1 | exec > ~/.logs/xsession 2>&1 2 | export LANG="en_GB.UTF-8" 3 | export LANGUAGE="en_GB.UTF-8" 4 | setxkbmap -option terminate:ctrl_alt_bksp 5 | setxkbmap -option ctrl:nocaps 6 | setxkbmap gb 7 | xset -dpms 8 | xset s off 9 | xsetroot -cursor_name left_ptr 10 | xset +fp ~/.local/share/fonts 11 | xset fp rehash 12 | xrdb ~/.Xresources 13 | [ ! -e /tmp/dwm.fifo ] && mkfifo /tmp/dwm.fifo 14 | 15 | nitrogen --restore & 16 | nm-applet & 17 | dunst & 18 | picom -b --experimental-backends --dbus & 19 | gnome-screensaver & 20 | /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1 & 21 | eval $(gnome-keyring-daemon -s --components=pkcs11,secrets,ssh,gpg) 22 | 23 | 24 | ~/.dwm/bin/status.sh 2> ~/.logs/status & 25 | ~/.dwm/dwm 2> ~/.logs/dwm 26 | -------------------------------------------------------------------------------- /bin/status.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | normal="\x01" 4 | red="\x05" 5 | yellow="\x06" 6 | green="\x07" 7 | blue="\x0A" 8 | 9 | getBattery() { 10 | perc=$(acpi -b | awk '/Battery/ {print $4}' | cut -d% -f1) 11 | time=$(acpi -b | awk '/Battery/ {print " (" substr($5,1,5)")"}') 12 | is_charging=$(acpi -a | awk '/Adapter/ {print $3}') 13 | battery="" 14 | 15 | if [ "${is_charging}" != "on-line" ]; then 16 | if [ ${perc} -le "25" ]; then 17 | battery="${red}${time}" 18 | elif [ ${perc} -le "50" ]; then 19 | battery="${yellow}${time}" 20 | elif [ ${perc} -le "75" ]; then 21 | battery="${yellow}${time}" 22 | elif [ ${perc} -le "100" ]; then 23 | battery="${green}${time}" 24 | fi 25 | fi 26 | 27 | echo -ne "${battery}" 28 | } 29 | 30 | getCPU() { 31 | cpu=$(mpstat -P ALL 1 1 | awk '/Average:/ && $2 ~ /all/ {print $3}') 32 | if [ $(bc <<< "${cpu}<25") == 1 ]; then 33 | echo -ne "${green}" 34 | elif [ $(bc <<< "${cpu}<75") == 1 ]; then 35 | echo -ne "${yellow}" 36 | else 37 | echo -ne "${red}" 38 | fi 39 | } 40 | 41 | getMEM() { 42 | if [ "$(free -V | awk '{print $4}' | cut -d. -f3)" -le 9 ]; then 43 | mem="$(free -m | awk '/-\/+/ {print $3}')" 44 | total=$(free -m | awk '/Mem:/ {print $2}') 45 | else 46 | total=$(free -m | awk '/Mem:/ {print $2}') 47 | mem="$(bc <<< ${total}-$(free -m | awk '/Mem:/ {print $7}'))" 48 | fi 49 | if [ $(bc <<< "${mem}<$(echo ${total}* .25 | bc)") == 1 ]; then 50 | echo -ne "${green}" 51 | elif [ $(bc <<< "${mem}<$(echo ${total}* .5 | bc)") == 1 ]; then 52 | echo -ne "${yellow}" 53 | else 54 | echo -ne "${red}" 55 | fi 56 | } 57 | 58 | getUpdates() { 59 | count="$(checkupdates | wc -l)" 60 | if [ $count -le 0 ]; then 61 | echo -ne "" 62 | else 63 | echo -ne "${blue} ${normal}${count} " 64 | fi 65 | } 66 | 67 | getSound() { 68 | is_muted=$(amixer get Master | awk '/%/ {gsub(/[\[\]]/,""); print $6}' | tail -1) 69 | cur_device=$(pactl list sinks | awk '/Active Port:/ {print substr($3,15)}' | grep -v "^$") 70 | if [ ${cur_device} == "headphones" ]; then 71 | out_device=("  " " ﳌ ") 72 | else 73 | out_device=(" 蓼 " " 遼 ") 74 | fi 75 | 76 | if [ ${is_muted} == "on" ]; then 77 | echo -ne "${green}${out_device[0]}" 78 | else 79 | echo -ne "${red}${out_device[1]}" 80 | fi 81 | 82 | } 83 | 84 | getMusic() { 85 | music_str="" 86 | eval $(sp eval) 87 | 88 | # if [ "$(mpc current)" != "" ]; then 89 | # if [ $(mpc | awk '/^\[/ {print $1}') == "[playing]" ]; then 90 | music_str+="${blue}" 91 | # else 92 | # music_str+="${blue}" 93 | # fi 94 | 95 | # music_str+=" $(mpc current) ($(mpc | head -2 | tail -1 | awk '{print $3}'))" 96 | # elif [ "$($HOME/bin/clem-np.sh)" != "" ]; then 97 | # local status="$($HOME/bin/clem-np.sh)" 98 | # if [ "$(echo $status | cut -d '|' -f1)" == "Playing" ]; then 99 | # music_str+="${blue}" 100 | # else 101 | # music_str+="${blue}" 102 | # fi 103 | 104 | # music_str+="$(echo $status | cut -d '|' -f2-)" 105 | # fi 106 | music_str+="${SPOTIFY_TITLE} - ${SPOTIFY_ARTIST}" 107 | echo -ne "${music_str} " 108 | } 109 | 110 | getTime() { 111 | tme="$(date '+%A %d/%m/%y %H:%M')" 112 | echo -ne "${blue} ${normal}${tme} " 113 | } 114 | 115 | while true; do 116 | xsetroot -name "$(getUpdates) $(getBattery) $(getSound) $(getCPU) $(getMEM) $(getTime) " 117 | done 118 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | #include "gaplessgrid.c" 2 | // Needed for media keys 3 | #include "X11/XF86keysym.h" 4 | 5 | #define NUMCOLORS 17 6 | #define ALTKEY Mod1Mask 7 | #define WINKEY Mod4Mask 8 | #define TAGKEYS(KEY,TAG) \ 9 | { ALTKEY, KEY, view, {.ui = 1 << TAG} }, \ 10 | { ALTKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ 11 | { ALTKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ 12 | { ALTKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, 13 | #define STACKKEYS(MOD,ACTION) \ 14 | { MOD, XK_q, ACTION##stack, {.i = 0 } }, \ 15 | { MOD, XK_a, ACTION##stack, {.i = 1 } }, \ 16 | { MOD, XK_z, ACTION##stack, {.i = 2 } }, \ 17 | { MOD, XK_x, ACTION##stack, {.i = -1 } }, 18 | #define INCSTACKKEYS(MOD,ACTION) \ 19 | { MOD, XK_grave, ACTION##stack, {.i = PREVSEL } }, \ 20 | { MOD, XK_j, ACTION##stack, {.i = INC(+1) } }, \ 21 | { MOD, XK_k, ACTION##stack, {.i = INC(-1) } }, 22 | 23 | static const unsigned int tagspacing = 2; /* space between tags */ 24 | static const unsigned int tagpadding = 50; /* inner padding of tags */ 25 | static const unsigned int taglinepx = 2; /* height of tag underline */ 26 | static const unsigned int systrayspacing = 2; /* systray spacing */ 27 | static const Bool showsystray = True; /* false means no systray */ 28 | static const unsigned int gappx = 8; /* gaps between windows */ 29 | static const unsigned int borderpx = 2; /* border pixel of windows */ 30 | static const unsigned int snap = 32; /* snap pixel */ 31 | static const Bool showbar = True; /* false means no bar */ 32 | static const Bool topbar = True; /* false means bottom bar */ 33 | static const float mfact = 0.50; /* factor of master area size [0.05..0.95] */ 34 | static const int nmaster = 1; /* number of clients in master area */ 35 | static const Bool resizehints = False; /* true means respect size hints in tiled resizals */ 36 | 37 | static const char *fonts[] = { 38 | "Hasklug Nerd Font:size=10", 39 | "Siji:size=10", 40 | }; 41 | 42 | static const char dmenufont[] = "Hasklug Nerd Font-8"; 43 | static const char dwmpath[] = "/home/elken/.dwm/dwm"; 44 | static const char termname[] = "alacritty"; 45 | 46 | #ifdef SOLARIZED_DARK 47 | static const char colors[NUMCOLORS][ColLast][13] = { 48 | /* border fg bg */ 49 | { "#286e75", "#286e75", "#002b36" }, /* [0] 01 - Client normal */ 50 | { "#af8700", "#268bd2", "#002b36" }, /* [1] 02 - Client selected */ 51 | { "#286e75", "#dc322f", "#002b36" }, /* [2] 03 - Client urgent */ 52 | { "#286e75", "#286e75", "#002b36" }, /* [3] 04 - Client occupied */ 53 | { "#002b36", "#dc322f", "#002b36" }, /* [4] 05 - Red */ 54 | { "#002b36", "#af8700", "#002b36" }, /* [5] 06 - Yellow */ 55 | { "#002b36", "#859900", "#002b36" }, /* [6] 07 - Green */ 56 | { "#002b36", "#666666", "#002b36" }, /* [7] 08 - Dark grey */ 57 | { "#002b36", "#DCDCDC", "#002b36" }, /* [8] 09 - Light grey */ 58 | { "#286e75", "#286e75", "#002b36" }, /* [9] 0A - Bar normal*/ 59 | { "#268bd2", "#268bd2", "#002b36" }, /* [10] 0B - Bar selected*/ 60 | { "#286e75", "#dc322f", "#002b36" }, /* [11] 0C - Bar urgent*/ 61 | { "#286e75", "#268bd2", "#002b36" }, /* [12] 0D - Bar occupied*/ 62 | { "#286e75", "#286e75", "#002b36" }, /* [13] 0E - Tag normal*/ 63 | { "#268bd2", "#268bd2", "#002b36" }, /* [14] 0F - Tag selected*/ 64 | { "#dc322f", "#dc322f", "#002b36" }, /* [15] 10 - Tag urgent*/ 65 | { "#286e75", "#268bd2", "#002b36" }, /* [16] 11 - Tag occupied*/ 66 | }; 67 | #endif 68 | 69 | #ifdef SOLARIZED_LIGHT 70 | static const char colors[NUMCOLORS][ColLast][13] = { 71 | /* border fg bg */ 72 | { "#93a1a1", "#93a1a1", "#fdf6e3" }, /* [0] 01 - Client normal */ 73 | { "#268bd2", "#268bd2", "#fdf6e3" }, /* [1] 02 - Client selected */ 74 | { "#93a1a1", "#dc322f", "#fdf6e3" }, /* [2] 03 - Client urgent */ 75 | { "#93a1a1", "#93a1a1", "#fdf6e3" }, /* [3] 04 - Client occupied */ 76 | { "#fdf6e3", "#dc322f", "#fdf6e3" }, /* [4] 05 - Red */ 77 | { "#fdf6e3", "#af8700", "#fdf6e3" }, /* [5] 06 - Yellow */ 78 | { "#fdf6e3", "#859900", "#fdf6e3" }, /* [6] 07 - Green */ 79 | { "#fdf6e3", "#666666", "#fdf6e3" }, /* [7] 08 - Dark grey */ 80 | { "#fdf6e3", "#DCDCDC", "#fdf6e3" }, /* [8] 09 - Light grey */ 81 | { "#93a1a1", "#93a1a1", "#fdf6e3" }, /* [9] 0A - Bar normal*/ 82 | { "#268bd2", "#268bd2", "#fdf6e3" }, /* [10] 0B - Bar selected*/ 83 | { "#93a1a1", "#dc322f", "#fdf6e3" }, /* [11] 0C - Bar urgent*/ 84 | { "#93a1a1", "#268bd2", "#fdf6e3" }, /* [12] 0D - Bar occupied*/ 85 | { "#93a1a1", "#93a1a1", "#fdf6e3" }, /* [13] 0E - Tag normal*/ 86 | { "#268bd2", "#268bd2", "#fdf6e3" }, /* [14] 0F - Tag selected*/ 87 | { "#dc322f", "#dc322f", "#fdf6e3" }, /* [15] 10 - Tag urgent*/ 88 | { "#93a1a1", "#268bd2", "#fdf6e3" }, /* [16] 11 - Tag occupied*/ 89 | }; 90 | #endif 91 | 92 | #ifdef NORD 93 | static const char colors[NUMCOLORS][ColLast][17] = { 94 | /* border fg bg */ 95 | { "#3b4252", "#d8dee9", "#2e3440" }, /* [0] 01 - Client normal */ 96 | { "#d8dee9", "#3b4252", "#2e3440" }, /* [1] 02 - Client selected */ 97 | { "#bf616a", "#d8dee9", "#2e3440" }, /* [2] 03 - Client urgent */ 98 | { "#88c0d0", "#88c0d0", "#2e3440" }, /* [3] 04 - Client occupied */ 99 | { "#2e3440", "#bf616a", "#2e3440" }, /* [4] 05 - Red */ 100 | { "#2e3440", "#ebcb8b", "#2e3440" }, /* [5] 06 - Yellow */ 101 | { "#2e3440", "#a3be8c", "#2e3440" }, /* [6] 07 - Green */ 102 | { "#2e3440", "#3b4252", "#2e3440" }, /* [7] 08 - Dark grey */ 103 | { "#2e3440", "#929aaa", "#2e3440" }, /* [8] 09 - Light grey */ 104 | { "#4c566a", "#4c566a", "#2e3440" }, /* [9] 0A - Bar normal*/ 105 | { "#4c566a", "#434c5e", "#2e3440" }, /* [10] 0B - Bar selected*/ 106 | { "#bf616a", "#bf616a", "#2e3440" }, /* [11] 0C - Bar urgent*/ 107 | { "#4c566a", "#8fbcbb", "#2e3440" }, /* [12] 0D - Bar occupied*/ 108 | { "#4c566a", "#4c566a", "#2e3440" }, /* [13] 0E - Tag normal*/ 109 | { "#8fbcbb", "#8fbcbb", "#2e3440" }, /* [14] 0F - Tag selected*/ 110 | { "#bf616a", "#bf616a", "#2e3440" }, /* [15] 10 - Tag urgent*/ 111 | { "#4c566a", "#d8dde9", "#2e3440" }, /* [16] 11 - Tag occupied*/ 112 | }; 113 | #endif 114 | 115 | #ifdef GRUVBOX 116 | static const char colors[NUMCOLORS][ColLast][17] = { 117 | /* border fg bg */ 118 | { "#282828", "#928374", "#282828" }, /* [0] 01 - Client normal */ 119 | { "#ebdbb2", "#458588", "#282828" }, /* [1] 02 - Client selected */ 120 | { "#83a598", "#fb4934", "#282828" }, /* [2] 03 - Client urgent */ 121 | { "#83a598", "#83a598", "#282828" }, /* [3] 04 - Client occupied */ 122 | { "#282828", "#fb4934", "#282828" }, /* [4] 05 - Red */ 123 | { "#282828", "#fabd2f", "#282828" }, /* [5] 06 - Yellow */ 124 | { "#282828", "#b8bb26", "#282828" }, /* [6] 07 - Green */ 125 | { "#282828", "#928374", "#282828" }, /* [7] 08 - Dark grey */ 126 | { "#282828", "#d5c4a1", "#282828" }, /* [8] 09 - Light grey */ 127 | { "#928374", "#928374", "#282828" }, /* [9] 0A - Bar normal*/ 128 | { "#3c3836", "#a89985", "#282828" }, /* [10] 0B - Bar selected*/ 129 | { "#fb4934", "#fb4934", "#282828" }, /* [11] 0C - Bar urgent*/ 130 | { "#928374", "#458588", "#282828" }, /* [12] 0D - Bar occupied*/ 131 | { "#3c3836", "#3c3836", "#282828" }, /* [13] 0E - Tag normal*/ 132 | { "#83a598", "#83a598", "#282828" }, /* [14] 0F - Tag selected*/ 133 | { "#fb4934", "#fb4934", "#282828" }, /* [15] 10 - Tag urgent*/ 134 | { "#3c3836", "#928374", "#282828" }, /* [16] 11 - Tag occupied*/ 135 | }; 136 | #endif 137 | 138 | static const Layout layouts[] = { 139 | /* symbol gaps arrange */ 140 | { "", True, tile }, 141 | { "", True, NULL }, 142 | { "", True, monocle }, 143 | { "", True, gaplessgrid }, 144 | { "", True, bstack }, 145 | }; 146 | 147 | static const Tag tags[] = { 148 | /* name layout mfact nmaster */ 149 | { "  ", &layouts[0], -1, -1 }, 150 | { "  ", &layouts[3], -1, -1 }, 151 | { "  ", &layouts[2], -1, -1 }, 152 | { "  ", &layouts[2], -1, -1 }, 153 | { "  ", &layouts[1], -1, -1 }, 154 | { "  ", &layouts[2], -1, -1 }, 155 | { "  ", &layouts[2], -1, -1 }, 156 | }; 157 | 158 | static const Rule rules[] = { 159 | /* xprop(1): 160 | * WM_CLASS(STRING) = instance, class 161 | * WM_NAME(STRING) = title 162 | */ 163 | /* class instance title tags mask isfloating monitor */ 164 | { "Firefox", NULL, NULL, 1 << 1, False, -1 }, 165 | { "firefox", NULL, NULL, 1 << 1, False, -1 }, 166 | { "Chromium", NULL, NULL, 1 << 1, False, -1 }, 167 | { "Icedove", NULL, NULL, 1 << 2, False, -1 }, 168 | { "Thunderbird", NULL, NULL, 1 << 2, False, -1 }, 169 | { "Steam", "Steam", "Steam", 1 << 6, False, -1 }, 170 | { "VirtualBox", NULL, NULL, 1 << 3, False, -1 }, 171 | { "Popcorntime", NULL, NULL, 1 << 5, False, -1 }, 172 | { "Transmission", NULL, NULL, 1 << 3, False, -1 }, 173 | { "mpv", "gl", NULL, 1 << 5, False, -1 }, 174 | { "Slack", "slack", NULL, 1 << 2, False, -1 }, 175 | { "Vlc", "vlc", NULL, 1 << 5, False, -1 }, 176 | { "Gnome-boxes", "gnome-boxes", NULL, 1 << 3, False, -1 } 177 | }; 178 | 179 | #ifdef SOLARIZED_DARK 180 | static const char *menu[] = { "dmenu_run", "-fn", dmenufont, "-nb", "#002b36", "-nf", "#568e75", "-sb", "#002b36", "-sf", "#268bd2", "-h", "22", NULL }; 181 | #endif 182 | 183 | #ifdef SOLARIZED_LIGHT 184 | static const char *menu[] = { "dmenu_run", "-fn", dmenufont, "-nb", "#fdf6e3", "-nf", "#93a1a1", "-sb", "#fdf6e3", "-sf", "#268bd2", "-h", "22", NULL }; 185 | #endif 186 | 187 | #ifdef GRUVBOX 188 | static const char *menu[] = { "dmenu_run", "-fn", dmenufont, "-nb", "#282828", "-nf", "#928374", "-sb", "#3c3836", "-sf", "#a89984", "-h", "22", NULL }; 189 | #endif 190 | 191 | #ifdef NORD 192 | static const char *menu[] = { "dmenu_run", "-fn", dmenufont, "-nb", "#2e3440", "-nf", "#d8dee9", "-sb", "#e5e9f0", "-sf", "#3b4252", "-h", "22", NULL }; 193 | #endif 194 | 195 | static const char *term[] = { termname, NULL }; 196 | static const char *webb[] = { "firefox", NULL }; 197 | static const char *mail[] = { "thunderbird", NULL }; 198 | static const char *edit[] = { "emacs", NULL }; 199 | static const char *mdia[] = { termname, "-e", "ncmpcpp", NULL }; 200 | static const char *file[] = { termname, "-e", "ranger", NULL }; 201 | 202 | // Media keys 203 | static const char *vold[] = { "amixer", "-q", "set", "Master", "5%-", "unmute", NULL }; 204 | static const char *volu[] = { "amixer", "-q", "set", "Master", "5%+", "unmute", NULL }; 205 | static const char *mute[] = { "amixer", "-q", "set", "Master", "toggle", NULL }; 206 | static const char *play[] = { "mpc", "toggle", NULL }; 207 | static const char *next[] = { "mpc", "next", NULL }; 208 | static const char *prev[] = { "mpc", "prev", NULL }; 209 | static const char *stop[] = { "mpc", "stop", NULL }; 210 | static const char *bklu[] = { "xbacklight", "-steps", "1", "-time", "0", "-inc", "5", NULL }; 211 | static const char *bkld[] = { "xbacklight", "-steps", "1", "-time", "0", "-dec", "5", NULL }; 212 | static const char *prts[] = { "xfce4-screenshooter", NULL}; 213 | 214 | static Key keys[] = { 215 | { ALTKEY, XK_p, spawn, {.v = menu } }, 216 | { WINKEY, XK_w, runorraise, {.v = webb } }, 217 | { WINKEY, XK_Return, spawn, {.v = term } }, 218 | { WINKEY, XK_e, spawn, {.v = edit } }, 219 | { WINKEY, XK_m, runorraise, {.v = mail } }, 220 | { WINKEY, XK_n, runorraise, {.v = mdia } }, 221 | { WINKEY, XK_f, spawn, {.v = file } }, 222 | {0, XF86XK_AudioLowerVolume, spawn, {.v = vold } }, 223 | {0, XF86XK_AudioRaiseVolume, spawn, {.v = volu } }, 224 | {0, XF86XK_AudioMute, spawn, {.v = mute } }, 225 | {0, XF86XK_AudioPlay, spawn, {.v = play } }, 226 | {0, XF86XK_AudioNext, spawn, {.v = next } }, 227 | {0, XF86XK_AudioPrev, spawn, {.v = prev } }, 228 | {0, XF86XK_AudioStop, spawn, {.v = stop } }, 229 | {0, XF86XK_MonBrightnessUp, spawn, {.v = bklu } }, 230 | {0, XF86XK_MonBrightnessDown, spawn, {.v = bkld } }, 231 | {0, XK_Print, spawn, {.v = prts } }, 232 | { ALTKEY|ShiftMask, XK_F12, quit, {0} }, 233 | { ALTKEY|ShiftMask, XK_BackSpace, self_restart, {0} }, 234 | { ALTKEY|ShiftMask, XK_b, togglebar, {0} }, 235 | { ALTKEY, XK_BackSpace, killclient, {0} }, 236 | { ALTKEY|ShiftMask, XK_Return, zoom, {0} }, 237 | { ALTKEY, XK_Tab, view, {0} }, 238 | { ALTKEY|ShiftMask, XK_f, togglefloating, {0} }, 239 | { ALTKEY, XK_space, setlayout, {0} }, 240 | { ALTKEY, XK_t, setlayout, {.v = &layouts[0] } }, 241 | { ALTKEY, XK_f, setlayout, {.v = &layouts[1] } }, 242 | { ALTKEY, XK_m, setlayout, {.v = &layouts[2] } }, 243 | { ALTKEY, XK_g, setlayout, {.v = &layouts[3] } }, 244 | { ALTKEY, XK_b, setlayout, {.v = &layouts[4] } }, 245 | { ALTKEY|ShiftMask, XK_h, setmfact, {.f = -0.05 } }, 246 | { ALTKEY|ShiftMask, XK_l, setmfact, {.f = +0.05 } }, 247 | { ALTKEY, XK_equal, incnmaster, {.i = +1 } }, 248 | { ALTKEY, XK_minus, incnmaster, {.i = -1 } }, 249 | { ALTKEY, XK_0, view, {.ui = ~0 } }, 250 | { ALTKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, 251 | { ALTKEY, XK_comma, focusmon, {.i = -1 } }, 252 | { ALTKEY, XK_period, focusmon, {.i = +1 } }, 253 | { ALTKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, 254 | { ALTKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, 255 | STACKKEYS(WINKEY, focus) 256 | STACKKEYS(WINKEY|ShiftMask, push) 257 | INCSTACKKEYS(ALTKEY, focus) 258 | INCSTACKKEYS(ALTKEY|ShiftMask, push) 259 | TAGKEYS( XK_1, 0) 260 | TAGKEYS( XK_2, 1) 261 | TAGKEYS( XK_3, 2) 262 | TAGKEYS( XK_4, 3) 263 | TAGKEYS( XK_5, 4) 264 | TAGKEYS( XK_6, 5) 265 | TAGKEYS( XK_7, 6) 266 | }; 267 | 268 | static Button buttons[] = { 269 | { ClkLtSymbol, 0, Button1, setlayout, {0} }, 270 | { ClkClientWin, ALTKEY, Button1, movemouse, {0} }, 271 | { ClkClientWin, ALTKEY, Button2, togglefloating, {0} }, 272 | { ClkClientWin, ALTKEY, Button3, resizemouse, {0} }, 273 | { ClkTagBar, 0, Button1, view, {0} }, 274 | { ClkTagBar, 0, Button3, toggleview, {0} }, 275 | { ClkTagBar, ALTKEY, Button1, tag, {0} }, 276 | { ClkTagBar, ALTKEY, Button3, toggletag, {0} }, 277 | }; 278 | static const char *dwmfifo = "/tmp/dwm.fifo"; 279 | static Command commands[] = { 280 | { "dmenu", spawn, {.v = menu} }, 281 | { "term", spawn, {.v = term} }, 282 | { "restart", self_restart, {0} }, 283 | { "togglebar", togglebar, {0} }, 284 | { "focusstack+", focusstack, {.i = +1} }, 285 | { "focusstack-", focusstack, {.i = -1} }, 286 | { "incnmaster+", incnmaster, {.i = +1} }, 287 | { "incnmaster-", incnmaster, {.i = -1} }, 288 | { "setmfact+", setmfact, {.f = +0.05} }, 289 | { "setmfact-", setmfact, {.f = -0.05} }, 290 | { "zoom", zoom, {0} }, 291 | { "view", view, {0} }, 292 | { "killclient", killclient, {0} }, 293 | { "setlayout-tiled", setlayout, {.v = &layouts[0]} }, 294 | { "setlayout-float", setlayout, {.v = &layouts[1]} }, 295 | { "setlayout-mono", setlayout, {.v = &layouts[2]} }, 296 | { "togglelayout", setlayout, {0} }, 297 | { "togglefloating", togglefloating, {0} }, 298 | { "viewall", view, {.ui = ~0} }, 299 | { "tag", tag, {.ui = ~0} }, 300 | { "focusmon+", focusmon, {.i = +1} }, 301 | { "focusmon-", focusmon, {.i = -1} }, 302 | { "tagmon+", tagmon, {.i = +1} }, 303 | { "tagmon-", tagmon, {.i = -1} }, 304 | { "view1", view, {.ui = 1 << 0} }, 305 | { "view2", view, {.ui = 1 << 1} }, 306 | { "view3", view, {.ui = 1 << 2} }, 307 | { "view4", view, {.ui = 1 << 3} }, 308 | { "view5", view, {.ui = 1 << 4} }, 309 | { "view6", view, {.ui = 1 << 5} }, 310 | { "view7", view, {.ui = 1 << 6} }, 311 | { "view8", view, {.ui = 1 << 7} }, 312 | { "view9", view, {.ui = 1 << 8} }, 313 | { "toggleview1", toggleview, {.ui = 1 << 0} }, 314 | { "toggleview2", toggleview, {.ui = 1 << 1} }, 315 | { "toggleview3", toggleview, {.ui = 1 << 2} }, 316 | { "toggleview4", toggleview, {.ui = 1 << 3} }, 317 | { "toggleview5", toggleview, {.ui = 1 << 4} }, 318 | { "toggleview6", toggleview, {.ui = 1 << 5} }, 319 | { "toggleview7", toggleview, {.ui = 1 << 6} }, 320 | { "toggleview8", toggleview, {.ui = 1 << 7} }, 321 | { "toggleview9", toggleview, {.ui = 1 << 8} }, 322 | { "tag1", tag, {.ui = 1 << 0} }, 323 | { "tag2", tag, {.ui = 1 << 1} }, 324 | { "tag3", tag, {.ui = 1 << 2} }, 325 | { "tag4", tag, {.ui = 1 << 3} }, 326 | { "tag5", tag, {.ui = 1 << 4} }, 327 | { "tag6", tag, {.ui = 1 << 5} }, 328 | { "tag7", tag, {.ui = 1 << 6} }, 329 | { "tag8", tag, {.ui = 1 << 7} }, 330 | { "tag9", tag, {.ui = 1 << 8} }, 331 | { "toggletag1", toggletag, {.ui = 1 << 0} }, 332 | { "toggletag2", toggletag, {.ui = 1 << 1} }, 333 | { "toggletag3", toggletag, {.ui = 1 << 2} }, 334 | { "toggletag4", toggletag, {.ui = 1 << 3} }, 335 | { "toggletag5", toggletag, {.ui = 1 << 4} }, 336 | { "toggletag6", toggletag, {.ui = 1 << 5} }, 337 | { "toggletag7", toggletag, {.ui = 1 << 6} }, 338 | { "toggletag8", toggletag, {.ui = 1 << 7} }, 339 | { "toggletag9", toggletag, {.ui = 1 << 8} }, 340 | }; 341 | -------------------------------------------------------------------------------- /config.mk: -------------------------------------------------------------------------------- 1 | # dwm version 2 | VERSION = 6.1 3 | 4 | # Customize below to fit your system 5 | 6 | # paths 7 | PREFIX = /usr 8 | MANPREFIX = ${PREFIX}/share/man 9 | 10 | X11INC = /usr/include/X11 11 | X11LIB = /usr/lib/X11 12 | 13 | # Xinerama, comment if you don't want it 14 | # XINERAMALIBS = -lXinerama 15 | # XINERAMAFLAGS = -DXINERAMA 16 | 17 | # includes and libs 18 | INCS = -I${X11INC} -I/usr/include/freetype2 19 | LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} -lfontconfig -lXft 20 | 21 | # flags 22 | CFLAGS = -g -std=c11 -pedantic -Wall -DVERSION=\"${VERSION}\" -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} -DNORD 23 | LDFLAGS = -s ${LIBS} 24 | 25 | # Solaris 26 | #CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" 27 | #LDFLAGS = ${LIBS} 28 | 29 | # compiler and linker 30 | CC = clang 31 | -------------------------------------------------------------------------------- /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 | #define UTF_SIZ 4 13 | 14 | static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; 15 | static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; 16 | static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; 17 | static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; 18 | 19 | static long 20 | utf8decodebyte(const char c, size_t *i) { 21 | for(*i = 0; *i < (UTF_SIZ + 1); ++(*i)) 22 | if(((unsigned char)c & utfmask[*i]) == utfbyte[*i]) 23 | return (unsigned char)c & ~utfmask[*i]; 24 | return 0; 25 | } 26 | 27 | static size_t 28 | utf8validate(long *u, size_t i) { 29 | if(!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) 30 | *u = UTF_INVALID; 31 | for(i = 1; *u > utfmax[i]; ++i) 32 | ; 33 | return i; 34 | } 35 | 36 | static size_t 37 | utf8decode(const char *c, long *u, size_t clen) { 38 | size_t i, j, len, type; 39 | long udecoded; 40 | 41 | *u = UTF_INVALID; 42 | if(!clen) 43 | return 0; 44 | udecoded = utf8decodebyte(c[0], &len); 45 | if(!BETWEEN(len, 1, UTF_SIZ)) 46 | return 1; 47 | for(i = 1, j = 1; i < clen && j < len; ++i, ++j) { 48 | udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); 49 | if(type != 0) 50 | return j; 51 | } 52 | if(j < len) 53 | return 0; 54 | *u = udecoded; 55 | utf8validate(u, len); 56 | return len; 57 | } 58 | 59 | Drw * 60 | drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) { 61 | Drw *drw = (Drw *)calloc(1, sizeof(Drw)); 62 | if(!drw) 63 | return NULL; 64 | drw->dpy = dpy; 65 | drw->screen = screen; 66 | drw->root = root; 67 | drw->w = w; 68 | drw->h = h; 69 | drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); 70 | drw->gc = XCreateGC(dpy, root, 0, NULL); 71 | drw->fontcount = 0; 72 | XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); 73 | return drw; 74 | } 75 | 76 | void 77 | drw_resize(Drw *drw, unsigned int w, unsigned int h) { 78 | if(!drw) 79 | return; 80 | drw->w = w; 81 | drw->h = h; 82 | if(drw->drawable != 0) 83 | XFreePixmap(drw->dpy, drw->drawable); 84 | drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); 85 | } 86 | 87 | void 88 | drw_free(Drw *drw) { 89 | size_t i; 90 | for (i = 0; i < drw->fontcount; i++) { 91 | drw_font_free(drw->fonts[i]); 92 | } 93 | XFreePixmap(drw->dpy, drw->drawable); 94 | XFreeGC(drw->dpy, drw->gc); 95 | free(drw); 96 | } 97 | 98 | /* This function is an implementation detail. Library users should use 99 | * drw_font_create instead. 100 | */ 101 | static Fnt * 102 | drw_font_xcreate(Drw *drw, const char *fontname, FcPattern *fontpattern) { 103 | Fnt *font; 104 | 105 | if (!(fontname || fontpattern)) 106 | die("No font specified.\n"); 107 | 108 | if (!(font = (Fnt *)calloc(1, sizeof(Fnt)))) 109 | return NULL; 110 | 111 | if (fontname) { 112 | /* Using the pattern found at font->xfont->pattern does not yield same 113 | * the same substitution results as using the pattern returned by 114 | * FcNameParse; using the latter results in the desired fallback 115 | * behaviour whereas the former just results in 116 | * missing-character-rectangles being drawn, at least with some fonts. 117 | */ 118 | if (!(font->xfont = XftFontOpenName(drw->dpy, drw->screen, fontname)) || 119 | !(font->pattern = FcNameParse((FcChar8 *) fontname))) { 120 | if (font->xfont) { 121 | XftFontClose(drw->dpy, font->xfont); 122 | font->xfont = NULL; 123 | } 124 | fprintf(stderr, "error, cannot load font: '%s'\n", fontname); 125 | } 126 | } else if (fontpattern) { 127 | if (!(font->xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { 128 | fprintf(stderr, "error, cannot load font pattern.\n"); 129 | } else { 130 | font->pattern = NULL; 131 | } 132 | } 133 | 134 | if (!font->xfont) { 135 | free(font); 136 | return NULL; 137 | } 138 | 139 | font->ascent = font->xfont->ascent; 140 | font->descent = font->xfont->descent; 141 | font->h = font->ascent + font->descent; 142 | font->dpy = drw->dpy; 143 | return font; 144 | } 145 | 146 | Fnt* 147 | drw_font_create(Drw *drw, const char *fontname) { 148 | return drw_font_xcreate(drw, fontname, NULL); 149 | } 150 | 151 | void 152 | drw_load_fonts(Drw* drw, const char *fonts[], size_t fontcount) { 153 | size_t i; 154 | Fnt *font; 155 | for (i = 0; i < fontcount; i++) { 156 | if (drw->fontcount >= DRW_FONT_CACHE_SIZE) { 157 | die("Font cache exhausted.\n"); 158 | } else if ((font = drw_font_xcreate(drw, fonts[i], NULL))) { 159 | drw->fonts[drw->fontcount++] = font; 160 | } 161 | } 162 | } 163 | 164 | void 165 | drw_font_free(Fnt *font) { 166 | if(!font) 167 | return; 168 | if(font->pattern) 169 | FcPatternDestroy(font->pattern); 170 | XftFontClose(font->dpy, font->xfont); 171 | free(font); 172 | } 173 | 174 | Clr * 175 | drw_clr_create(Drw *drw, const char *clrname) { 176 | Clr *clr; 177 | Colormap cmap; 178 | Visual *vis; 179 | 180 | if(!drw) 181 | return NULL; 182 | clr = (Clr *)calloc(1, sizeof(Clr)); 183 | if(!clr) 184 | return NULL; 185 | cmap = DefaultColormap(drw->dpy, drw->screen); 186 | vis = DefaultVisual(drw->dpy, drw->screen); 187 | if(!XftColorAllocName(drw->dpy, vis, cmap, clrname, &clr->rgb)) 188 | die("error, cannot allocate color '%s'\n", clrname); 189 | clr->pix = clr->rgb.pixel; 190 | return clr; 191 | } 192 | 193 | void 194 | drw_clr_free(Clr *clr) { 195 | if(clr) 196 | free(clr); 197 | } 198 | 199 | void 200 | drw_setscheme(Drw *drw, ClrScheme *scheme) { 201 | if(drw && scheme) 202 | drw->scheme = scheme; 203 | } 204 | 205 | int 206 | drw_colored_text(Drw *drw, ClrScheme *scheme, int numcolors, int x, int y, unsigned int w, unsigned int h, char *text) { 207 | if(!drw || !drw->fontcount || !drw->scheme) 208 | return 0; 209 | 210 | char *buf = text, *ptr = buf, c =1; 211 | int i; 212 | 213 | while(*ptr) { 214 | for(i = 0; *ptr < 0 || *ptr > numcolors; i++, ptr++); 215 | if(!*ptr) 216 | break; 217 | c = *ptr; 218 | *ptr = 0; 219 | if(i) { 220 | x = drw_text(drw, x, y, w, h, buf, 0); 221 | } 222 | *ptr = c; 223 | drw_setscheme(drw, &scheme[c-1]); 224 | buf = ++ptr; 225 | } 226 | drw_text(drw, x, y, w, h, buf, 0); 227 | return x; 228 | } 229 | 230 | void 231 | drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int empty) { 232 | int dx; 233 | 234 | if(!drw || !drw->fontcount || !drw->scheme) 235 | return; 236 | XSetForeground(drw->dpy, drw->gc, drw->scheme->fg->pix); 237 | dx = (drw->fonts[0]->ascent + drw->fonts[0]->descent + 2) / 4; 238 | if(filled) 239 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x+1, y+1, dx+1, dx+1); 240 | else if(empty) 241 | XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x+1, y+1, dx, dx); 242 | } 243 | 244 | int 245 | drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int invert) { 246 | char buf[1024]; 247 | int tx, ty; 248 | Extnts tex; 249 | Colormap cmap; 250 | Visual *vis; 251 | XftDraw *d; 252 | Fnt *curfont, *nextfont; 253 | size_t i, len; 254 | int utf8strlen, utf8charlen, render; 255 | long utf8codepoint = 0; 256 | const char *utf8str; 257 | FcCharSet *fccharset; 258 | FcPattern *fcpattern; 259 | FcPattern *match; 260 | XftResult result; 261 | int charexists = 0; 262 | 263 | if (!(render = x || y || w || h)) { 264 | w = ~w; 265 | } 266 | 267 | if (!drw || !drw->scheme) { 268 | return 0; 269 | } else if (render) { 270 | XSetForeground(drw->dpy, drw->gc, drw->scheme->bg->pix); 271 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); 272 | } 273 | 274 | if (!text || !drw->fontcount) { 275 | return 0; 276 | } else if (render) { 277 | cmap = DefaultColormap(drw->dpy, drw->screen); 278 | vis = DefaultVisual(drw->dpy, drw->screen); 279 | d = XftDrawCreate(drw->dpy, drw->drawable, vis, cmap); 280 | } 281 | 282 | curfont = drw->fonts[0]; 283 | while (1) { 284 | utf8strlen = 0; 285 | utf8str = text; 286 | nextfont = NULL; 287 | while (*text) { 288 | utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); 289 | for (i = 0; i < drw->fontcount; i++) { 290 | charexists = charexists || XftCharExists(drw->dpy, drw->fonts[i]->xfont, utf8codepoint); 291 | if (charexists) { 292 | if (drw->fonts[i] == curfont) { 293 | utf8strlen += utf8charlen; 294 | text += utf8charlen; 295 | } else { 296 | nextfont = drw->fonts[i]; 297 | } 298 | break; 299 | } 300 | } 301 | 302 | if (!charexists || (nextfont && nextfont != curfont)) { 303 | break; 304 | } else { 305 | charexists = 0; 306 | } 307 | } 308 | 309 | if (utf8strlen) { 310 | drw_font_getexts(curfont, utf8str, utf8strlen, &tex); 311 | /* shorten text if necessary */ 312 | for(len = MIN(utf8strlen, (sizeof buf) - 1); len && (tex.w > w - drw->fonts[0]->h || w < drw->fonts[0]->h); len--) 313 | drw_font_getexts(curfont, utf8str, len, &tex); 314 | 315 | if (len) { 316 | memcpy(buf, utf8str, len); 317 | buf[len] = '\0'; 318 | if(len < utf8strlen) 319 | for(i = len; i && i > len - 3; buf[--i] = '.'); 320 | 321 | if (render) { 322 | /* th = pad ? (curfont->ascent + curfont->descent) : 0; */ 323 | ty = y + ((h + curfont->ascent - curfont->descent) / 2); 324 | tx = x + (h / 2); 325 | XftDrawStringUtf8(d, &drw->scheme->fg->rgb, curfont->xfont, tx, ty, (XftChar8 *)buf, len); 326 | } 327 | 328 | x += tex.w; 329 | w -= tex.w; 330 | } 331 | } 332 | 333 | if (!*text) { 334 | break; 335 | } else if (nextfont) { 336 | charexists = 0; 337 | curfont = nextfont; 338 | } else { 339 | /* Regardless of whether or not a fallback font is found, the 340 | * character must be drawn. 341 | */ 342 | charexists = 1; 343 | 344 | if (drw->fontcount >= DRW_FONT_CACHE_SIZE) { 345 | continue; 346 | } 347 | 348 | fccharset = FcCharSetCreate(); 349 | FcCharSetAddChar(fccharset, utf8codepoint); 350 | 351 | if (!drw->fonts[0]->pattern) { 352 | /* Refer to the comment in drw_font_xcreate for more 353 | * information. 354 | */ 355 | die("The first font in the cache must be loaded from a font string.\n"); 356 | } 357 | 358 | fcpattern = FcPatternDuplicate(drw->fonts[0]->pattern); 359 | FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); 360 | FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); 361 | 362 | FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); 363 | FcDefaultSubstitute(fcpattern); 364 | match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); 365 | 366 | FcCharSetDestroy(fccharset); 367 | FcPatternDestroy(fcpattern); 368 | 369 | if (match) { 370 | curfont = drw_font_xcreate(drw, NULL, match); 371 | if (curfont && XftCharExists(drw->dpy, curfont->xfont, utf8codepoint)) { 372 | drw->fonts[drw->fontcount++] = curfont; 373 | } else { 374 | if (curfont) { 375 | drw_font_free(curfont); 376 | } 377 | curfont = drw->fonts[0]; 378 | } 379 | } 380 | } 381 | } 382 | 383 | if (render) { 384 | XftDrawDestroy(d); 385 | } 386 | 387 | return x; 388 | } 389 | 390 | void 391 | drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) { 392 | if(!drw) 393 | return; 394 | XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); 395 | XSync(drw->dpy, False); 396 | } 397 | 398 | 399 | void 400 | drw_font_getexts(Fnt *font, const char *text, unsigned int len, Extnts *tex) { 401 | XGlyphInfo ext; 402 | 403 | if(!font || !text) 404 | return; 405 | XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); 406 | tex->h = font->h; 407 | tex->w = ext.xOff; 408 | } 409 | 410 | unsigned int 411 | drw_font_getexts_width(Fnt *font, const char *text, unsigned int len) { 412 | Extnts tex; 413 | 414 | if(!font) 415 | return -1; 416 | drw_font_getexts(font, text, len, &tex); 417 | return tex.w; 418 | } 419 | 420 | Cur * 421 | drw_cur_create(Drw *drw, int shape) { 422 | Cur *cur = (Cur *)calloc(1, sizeof(Cur)); 423 | 424 | if(!drw || !cur) 425 | return NULL; 426 | cur->cursor = XCreateFontCursor(drw->dpy, shape); 427 | return cur; 428 | } 429 | 430 | void 431 | drw_cur_free(Drw *drw, Cur *cursor) { 432 | if(!drw || !cursor) 433 | return; 434 | XFreeCursor(drw->dpy, cursor->cursor); 435 | free(cursor); 436 | } 437 | -------------------------------------------------------------------------------- /drw.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #define DRW_FONT_CACHE_SIZE 32 3 | 4 | typedef struct { 5 | unsigned long pix; 6 | XftColor rgb; 7 | } Clr; 8 | 9 | typedef struct { 10 | Cursor cursor; 11 | } Cur; 12 | 13 | typedef struct { 14 | Display *dpy; 15 | int ascent; 16 | int descent; 17 | unsigned int h; 18 | XftFont *xfont; 19 | FcPattern *pattern; 20 | } Fnt; 21 | 22 | typedef struct { 23 | Clr *fg; 24 | Clr *bg; 25 | Clr *border; 26 | } ClrScheme; 27 | 28 | typedef struct { 29 | unsigned int w, h; 30 | Display *dpy; 31 | int screen; 32 | Window root; 33 | Drawable drawable; 34 | GC gc; 35 | ClrScheme *scheme; 36 | size_t fontcount; 37 | Fnt *fonts[DRW_FONT_CACHE_SIZE]; 38 | } Drw; 39 | 40 | typedef struct { 41 | unsigned int w; 42 | unsigned int h; 43 | } Extnts; 44 | 45 | /* Drawable abstraction */ 46 | Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); 47 | void drw_resize(Drw *drw, unsigned int w, unsigned int h); 48 | void drw_free(Drw *drw); 49 | 50 | /* Fnt abstraction */ 51 | Fnt *drw_font_create(Drw *drw, const char *fontname); 52 | void drw_load_fonts(Drw* drw, const char *fonts[], size_t fontcount); 53 | void drw_font_free(Fnt *font); 54 | void drw_font_getexts(Fnt *font, const char *text, unsigned int len, Extnts *extnts); 55 | unsigned int drw_font_getexts_width(Fnt *font, const char *text, unsigned int len); 56 | 57 | /* Colour abstraction */ 58 | Clr *drw_clr_create(Drw *drw, const char *clrname); 59 | void drw_clr_free(Clr *clr); 60 | 61 | /* Cursor abstraction */ 62 | Cur *drw_cur_create(Drw *drw, int shape); 63 | void drw_cur_free(Drw *drw, Cur *cursor); 64 | 65 | /* Drawing context manipulation */ 66 | void drw_setfont(Drw *drw, Fnt *font); 67 | void drw_setscheme(Drw *drw, ClrScheme *scheme); 68 | 69 | /* Drawing functions */ 70 | int drw_colored_text(Drw *drw, ClrScheme *scheme, int numcolors, int x, int y, unsigned int w, unsigned int h, char *text); 71 | void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int empty); 72 | int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int pad); 73 | 74 | /* Map functions */ 75 | void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); 76 | -------------------------------------------------------------------------------- /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 contains the window which currently needs most attention, whereas the 14 | stacking area contains all other windows. In monocle layout all windows are 15 | maximised to the screen size. In floating layout windows can be resized and 16 | moved freely. Dialog windows are always managed floating, regardless of the 17 | layout applied. 18 | .P 19 | Windows are grouped by tags. Each window can be tagged with one or multiple 20 | tags. Selecting certain tags displays all windows with these tags. 21 | .P 22 | Each screen contains a small status bar which displays all available tags, the 23 | layout, the title of the focused window, and the text read from the root window 24 | name property, if the screen is focused. A floating window is indicated with an 25 | empty square and a maximised floating window is indicated with a filled square 26 | before the windows title. The selected tags are indicated with a different 27 | color. The tags of the focused window are indicated with a filled square in the 28 | top left corner. The tags which are applied to one or more windows are 29 | indicated with an empty square in the top left corner. 30 | .P 31 | dwm draws a small border around windows to indicate the focus state. 32 | .SH OPTIONS 33 | .TP 34 | .B \-v 35 | prints version information to standard output, then exits. 36 | .SH USAGE 37 | .SS Status bar 38 | .TP 39 | .B X root window name 40 | is read and displayed in the status text area. It can be set with the 41 | .BR xsetroot (1) 42 | command. 43 | .TP 44 | .B Button1 45 | click on a tag label to display all windows with that tag, click on the layout 46 | label toggles between tiled and floating layout. 47 | .TP 48 | .B Button3 49 | click on a tag label adds/removes all windows with that tag to/from the view. 50 | .TP 51 | .B Mod1\-Button1 52 | click on a tag label applies that tag to the focused window. 53 | .TP 54 | .B Mod1\-Button3 55 | click on a tag label adds/removes that tag to/from the focused window. 56 | .SS Keyboard commands 57 | .TP 58 | .B Mod1\-Shift\-Return 59 | Start 60 | .BR st(1). 61 | .TP 62 | .B Mod1\-, 63 | Focus previous screen, if any. 64 | .TP 65 | .B Mod1\-. 66 | Focus next screen, if any. 67 | .TP 68 | .B Mod1\-Shift\-, 69 | Send focused window to previous screen, if any. 70 | .TP 71 | .B Mod1\-Shift\-. 72 | Send focused window to next screen, if any. 73 | .TP 74 | .B Mod1\-b 75 | Toggles bar on and off. 76 | .TP 77 | .B Mod1\-t 78 | Sets tiled layout. 79 | .TP 80 | .B Mod1\-f 81 | Sets floating layout. 82 | .TP 83 | .B Mod1\-m 84 | Sets monocle layout. 85 | .TP 86 | .B Mod1\-space 87 | Toggles between current and previous layout. 88 | .TP 89 | .B Mod1\-j 90 | Focus next window. 91 | .TP 92 | .B Mod1\-k 93 | Focus previous window. 94 | .TP 95 | .B Mod1\-i 96 | Increase clients in master area. 97 | .TP 98 | .B Mod1\-d 99 | Decrease clients in master area. 100 | .TP 101 | .B Mod1\-l 102 | Increase master area size. 103 | .TP 104 | .B Mod1\-h 105 | Decrease master area size. 106 | .TP 107 | .B Mod1\-Return 108 | Zooms/cycles focused window to/from master area (tiled layouts only). 109 | .TP 110 | .B Mod1\-Shift\-c 111 | Close focused window. 112 | .TP 113 | .B Mod1\-Shift\-space 114 | Toggle focused window between tiled and floating state. 115 | .TP 116 | .B Mod1\-Tab 117 | Toggles to the previously selected tags. 118 | .TP 119 | .B Mod1\-Shift\-[1..n] 120 | Apply nth tag to focused window. 121 | .TP 122 | .B Mod1\-Shift\-0 123 | Apply all tags to focused window. 124 | .TP 125 | .B Mod1\-Control\-Shift\-[1..n] 126 | Add/remove nth tag to/from focused window. 127 | .TP 128 | .B Mod1\-[1..n] 129 | View all windows with nth tag. 130 | .TP 131 | .B Mod1\-0 132 | View all windows with any tag. 133 | .TP 134 | .B Mod1\-Control\-[1..n] 135 | Add/remove all windows with nth tag to/from the view. 136 | .TP 137 | .B Mod1\-Shift\-q 138 | Quit dwm. 139 | .SS Mouse commands 140 | .TP 141 | .B Mod1\-Button1 142 | Move focused window while dragging. Tiled windows will be toggled to the floating state. 143 | .TP 144 | .B Mod1\-Button2 145 | Toggles focused window between floating and tiled state. 146 | .TP 147 | .B Mod1\-Button3 148 | Resize focused window while dragging. Tiled windows will be toggled to the floating state. 149 | .SH CUSTOMIZATION 150 | dwm is customized by creating a custom config.h and (re)compiling the source 151 | code. This keeps it fast, secure and simple. 152 | .SH SEE ALSO 153 | .BR dmenu (1), 154 | .BR st (1) 155 | .SH BUGS 156 | Java applications which use the XToolkit/XAWT backend may draw grey windows 157 | only. The XToolkit/XAWT backend breaks ICCCM-compliance in recent JDK 1.5 and early 158 | JDK 1.6 versions, because it assumes a reparenting window manager. Possible workarounds 159 | are using JDK 1.4 (which doesn't contain the XToolkit/XAWT backend) or setting the 160 | environment variable 161 | .BR AWT_TOOLKIT=MToolkit 162 | (to use the older Motif backend instead) or running 163 | .B xprop -root -f _NET_WM_NAME 32a -set _NET_WM_NAME LG3D 164 | or 165 | .B wmname LG3D 166 | (to pretend that a non-reparenting window manager is running that the 167 | XToolkit/XAWT backend can recognize) or when using OpenJDK setting the environment variable 168 | .BR _JAVA_AWT_WM_NONREPARENTING=1 . 169 | .P 170 | GTK 2.10.9+ versions contain a broken 171 | .BR Save\-As 172 | file dialog implementation, 173 | which requests to reconfigure its window size in an endless loop. However, its 174 | window is still respondable during this state, so you can simply ignore the flicker 175 | until a new GTK version appears, which will fix this bug, approximately 176 | GTK 2.10.12+ versions. 177 | -------------------------------------------------------------------------------- /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 | #include 41 | #include 42 | #include 43 | #ifdef XINERAMA 44 | #include 45 | #endif /* XINERAMA */ 46 | #include 47 | 48 | #include "drw.h" 49 | #include "util.h" 50 | 51 | /* macros */ 52 | #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) 53 | #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) 54 | #define GETINC(X) ((X) - 2000) 55 | #define INC(X) ((X) + 2000) 56 | #define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ 57 | * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) 58 | #define ISINC(X) ((X) > 1000 && (X) < 3000) 59 | #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) 60 | #define PREVSEL 3000 61 | #define LENGTH(X) (sizeof X / sizeof X[0]) 62 | #define MAXCOLORS 17 63 | #define MOD(N,M) ((N)%(M) < 0 ? (N)%(M) + (M) : (N)%(M)) 64 | #define MOUSEMASK (BUTTONMASK|PointerMotionMask) 65 | #define WIDTH(X) ((X)->w + 2 * (X)->bw) 66 | #define HEIGHT(X) ((X)->h + 2 * (X)->bw) 67 | #define TAGMASK ((1 << LENGTH(tags)) - 1) 68 | #define TEXTW(X) (drw_text(drw, 0, 0, 0, 0, (X), 0) + drw->fonts[0]->h) 69 | #define TRUNC(X,A,B) (MAX((A), MIN((X), (B)))) 70 | 71 | #define SYSTEM_TRAY_REQUEST_DOCK 0 72 | #define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0 73 | 74 | /* XEMBED messages */ 75 | #define XEMBED_EMBEDDED_NOTIFY 0 76 | #define XEMBED_WINDOW_ACTIVATE 1 77 | #define XEMBED_FOCUS_IN 4 78 | #define XEMBED_MODALITY_ON 10 79 | 80 | #define XEMBED_MAPPED (1 << 0) 81 | #define XEMBED_WINDOW_ACTIVATE 1 82 | #define XEMBED_WINDOW_DEACTIVATE 2 83 | 84 | #define VERSION_MAJOR 0 85 | #define VERSION_MINOR 0 86 | #define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR 87 | 88 | /* enums */ 89 | enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ 90 | enum { SchemeNorm, SchemeSel, SchemeLast }; /* color schemes */ 91 | enum { NetSupported, NetWMName, NetWMState, 92 | NetWMFullscreen, NetActiveWindow, NetWMWindowType, 93 | NetWMWindowTypeDialog, NetClientList, NetSystemTray, 94 | NetSystemTrayOP, NetSystemTrayOrientation, NetLast }; /* EWMH atoms */ 95 | enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ 96 | enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ 97 | enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkClientWin, 98 | ClkRootWin, ClkLast }; /* clicks */ 99 | enum { ColBorder, ColFG, ColBG, ColLast }; 100 | 101 | typedef union { 102 | int i; 103 | unsigned int ui; 104 | float f; 105 | const void *v; 106 | } Arg; 107 | 108 | typedef struct { 109 | unsigned int click; 110 | unsigned int mask; 111 | unsigned int button; 112 | void (*func)(const Arg *arg); 113 | const Arg arg; 114 | } Button; 115 | 116 | typedef struct Monitor Monitor; 117 | typedef struct Client Client; 118 | struct Client { 119 | char name[256]; 120 | float mina, maxa; 121 | int x, y, w, h; 122 | int oldx, oldy, oldw, oldh; 123 | int basew, baseh, incw, inch, maxw, maxh, minw, minh; 124 | int bw, oldbw; 125 | unsigned int tags; 126 | Bool isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; 127 | Client *next; 128 | Client *snext; 129 | Monitor *mon; 130 | Window win; 131 | }; 132 | 133 | typedef struct { 134 | unsigned int mod; 135 | KeySym keysym; 136 | void (*func)(const Arg *); 137 | const Arg arg; 138 | } Key; 139 | 140 | typedef struct { 141 | const char *symbol; 142 | Bool addgaps; 143 | void (*arrange)(Monitor *); 144 | } Layout; 145 | 146 | struct Monitor { 147 | char ltsymbol[16]; 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 | unsigned int seltags; 153 | unsigned int sellt; 154 | unsigned int tagset[2]; 155 | Bool showbar; 156 | Bool topbar; 157 | Client *clients; 158 | Client *sel; 159 | Client *stack; 160 | Monitor *next; 161 | Window barwin; 162 | const Layout *lt[2]; 163 | int curtag; 164 | int prevtag; 165 | const Layout **lts; 166 | double *mfacts; 167 | int *nmasters; 168 | }; 169 | 170 | typedef struct { 171 | const char *name; 172 | const Layout *layout; 173 | float mfact; 174 | int nmaster; 175 | } Tag; 176 | 177 | typedef struct { 178 | const char *class; 179 | const char *instance; 180 | const char *title; 181 | unsigned int tags; 182 | Bool isfloating; 183 | int monitor; 184 | } Rule; 185 | 186 | typedef struct Systray Systray; 187 | struct Systray { 188 | Window win; 189 | Client *icons; 190 | }; 191 | 192 | typedef struct { 193 | const char *name; 194 | void (*func)(const Arg *arg); 195 | const Arg arg; 196 | } Command; 197 | 198 | /* function declarations */ 199 | static void applyrules(Client *c); 200 | static Bool applysizehints(Client *c, int *x, int *y, int *w, int *h, Bool interact); 201 | static void arrange(Monitor *m); 202 | static void arrangemon(Monitor *m); 203 | static void attach(Client *c); 204 | static void attachaside(Client *c); 205 | static void attachstack(Client *c); 206 | static void buttonpress(XEvent *e); 207 | static void checkotherwm(void); 208 | static void cleanup(void); 209 | static void cleanupmon(Monitor *mon); 210 | static void clearurgent(Client *c); 211 | static void clientmessage(XEvent *e); 212 | static void configure(Client *c); 213 | static void configurenotify(XEvent *e); 214 | static void configurerequest(XEvent *e); 215 | static Monitor *createmon(void); 216 | static void destroynotify(XEvent *e); 217 | static void detach(Client *c); 218 | static void detachstack(Client *c); 219 | static Monitor *dirtomon(int dir); 220 | static void dispatchcmd(void); 221 | static void drawbar(Monitor *m); 222 | static void drawbars(void); 223 | static void enternotify(XEvent *e); 224 | static Bool evpredicate(); 225 | static void expose(XEvent *e); 226 | static void focus(Client *c); 227 | static void focusin(XEvent *e); 228 | static void focusmon(const Arg *arg); 229 | static void focusstack(const Arg *arg); 230 | static Bool getrootptr(int *x, int *y); 231 | static long getstate(Window w); 232 | static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size); 233 | static void grabbuttons(Client *c, Bool focused); 234 | static void grabkeys(void); 235 | static void incnmaster(const Arg *arg); 236 | static void keypress(XEvent *e); 237 | static void killclient(const Arg *arg); 238 | static void manage(Window w, XWindowAttributes *wa); 239 | static void mappingnotify(XEvent *e); 240 | static void maprequest(XEvent *e); 241 | static void monocle(Monitor *m); 242 | static void motionnotify(XEvent *e); 243 | static void movemouse(const Arg *arg); 244 | static Client *nexttiled(Client *c); 245 | static void pop(Client *); 246 | static void propertynotify(XEvent *e); 247 | static void pushstack(const Arg *arg); 248 | static void quit(const Arg *arg); 249 | static Monitor *recttomon(int x, int y, int w, int h); 250 | static void resize(Client *c, int x, int y, int w, int h, Bool interact); 251 | static void resizeclient(Client *c, int x, int y, int w, int h); 252 | static void resizemouse(const Arg *arg); 253 | static void restack(Monitor *m); 254 | static void run(void); 255 | static void runorraise(const Arg *arg); 256 | static void scan(void); 257 | static Bool sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4); 258 | static void self_restart(const Arg *arg); 259 | static void sendmon(Client *c, Monitor *m); 260 | static void setclientstate(Client *c, long state); 261 | static void setfocus(Client *c); 262 | static void setfullscreen(Client *c, Bool fullscreen); 263 | static void setlayout(const Arg *arg); 264 | static void setmfact(const Arg *arg); 265 | static void setup(void); 266 | static void showhide(Client *c); 267 | static void sigchld(int unused); 268 | static void spawn(const Arg *arg); 269 | static int stackpos(const Arg *arg); 270 | static void tag(const Arg *arg); 271 | static void tagmon(const Arg *arg); 272 | static void tile(Monitor *); 273 | static void togglebar(const Arg *arg); 274 | static void togglefloating(const Arg *arg); 275 | static void toggletag(const Arg *arg); 276 | static void toggleview(const Arg *arg); 277 | static void unfocus(Client *c, Bool setfocus); 278 | static void unmanage(Client *c, Bool destroyed); 279 | static void unmapnotify(XEvent *e); 280 | static Bool updategeom(void); 281 | static void updatebarpos(Monitor *m); 282 | static void updatebars(void); 283 | static void updateclientlist(void); 284 | static void updatenumlockmask(void); 285 | static void updatesizehints(Client *c); 286 | static void updatestatus(void); 287 | static void updatewindowtype(Client *c); 288 | static void updatetitle(Client *c); 289 | static void updatewmhints(Client *c); 290 | static void view(const Arg *arg); 291 | static Client *wintoclient(Window w); 292 | static Monitor *wintomon(Window w); 293 | static int xerror(Display *dpy, XErrorEvent *ee); 294 | static int xerrordummy(Display *dpy, XErrorEvent *ee); 295 | static int xerrorstart(Display *dpy, XErrorEvent *ee); 296 | static void zoom(const Arg *arg); 297 | static void bstack(Monitor *m); 298 | static Atom getatomprop(Client *c, Atom prop); 299 | static unsigned int getsystraywidth(); 300 | static void removesystrayicon(Client *i); 301 | static void resizebarwin(Monitor *m); 302 | static void resizerequest(XEvent *e); 303 | static void updatesystray(void); 304 | static void updatesystrayicongeom(Client *i, int w, int h); 305 | static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); 306 | static Client *wintosystrayicon(Window w); 307 | 308 | /* variables */ 309 | static const char broken[] = "broken"; 310 | static char stext[256]; 311 | static int screen; 312 | static int sw, sh; /* X display screen geometry width, height */ 313 | static int bh, blw = 0; /* bar geometry */ 314 | static int (*xerrorxlib)(Display *, XErrorEvent *); 315 | static unsigned int numlockmask = 0; 316 | static void (*handler[LASTEvent]) (XEvent *) = { 317 | [ButtonPress] = buttonpress, 318 | [ClientMessage] = clientmessage, 319 | [ConfigureRequest] = configurerequest, 320 | [ConfigureNotify] = configurenotify, 321 | [DestroyNotify] = destroynotify, 322 | [EnterNotify] = enternotify, 323 | [Expose] = expose, 324 | [FocusIn] = focusin, 325 | [KeyPress] = keypress, 326 | [MappingNotify] = mappingnotify, 327 | [MapRequest] = maprequest, 328 | [MotionNotify] = motionnotify, 329 | [PropertyNotify] = propertynotify, 330 | [ResizeRequest] = resizerequest, 331 | [UnmapNotify] = unmapnotify 332 | }; 333 | static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; 334 | static Bool running = True; 335 | static Cur *cursor[CurLast]; 336 | static ClrScheme scheme[MAXCOLORS]; 337 | static Display *dpy; 338 | static Drw *drw; 339 | static Monitor *mons, *selmon; 340 | static Window root; 341 | static int gap; 342 | static Systray *systray = NULL; 343 | static unsigned long systrayorientation = _NET_SYSTEM_TRAY_ORIENTATION_HORZ; 344 | static int fifofd; 345 | 346 | /* configuration, allows nested code to access above variables */ 347 | #include "config.h" 348 | 349 | /* compile-time check if all tags fit into an unsigned int bit array. */ 350 | struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; 351 | 352 | /* function implementations */ 353 | void 354 | applyrules(Client *c) { 355 | const char *class, *instance; 356 | unsigned int i; 357 | const Rule *r; 358 | Monitor *m; 359 | XClassHint ch = { NULL, NULL }; 360 | 361 | /* rule matching */ 362 | c->isfloating = 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->isfloating = r->isfloating; 374 | c->tags |= r->tags; 375 | for(m = mons; m && m->num != r->monitor; m = m->next); 376 | if(m) 377 | c->mon = m; 378 | } 379 | } 380 | if(ch.res_class) 381 | XFree(ch.res_class); 382 | if(ch.res_name) 383 | XFree(ch.res_name); 384 | c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; 385 | } 386 | 387 | void 388 | adjustborders(Monitor *m) { 389 | Client *c, *l = NULL; 390 | int visible = 0; 391 | 392 | for(c = m->clients; c; c = c->next) { 393 | if (ISVISIBLE(c) && !c->isfloating && m->lt[m->sellt]->arrange) { 394 | if (m->lt[m->sellt]->arrange == monocle) { 395 | visible = 1; 396 | c->oldbw = c->bw; 397 | c->bw = 0; 398 | } else { 399 | visible++; 400 | c->oldbw = c->bw; 401 | c->bw = borderpx; 402 | } 403 | 404 | l = c; 405 | } 406 | } 407 | 408 | if (l && visible == 1 && l->bw) { 409 | l->oldbw = l->bw; 410 | l->bw = 0; 411 | resizeclient(l, l->x, l->y, l->w, l->h); 412 | } 413 | } 414 | 415 | Bool 416 | applysizehints(Client *c, int *x, int *y, int *w, int *h, Bool interact) { 417 | Bool baseismin; 418 | Monitor *m = c->mon; 419 | 420 | /* set minimum possible */ 421 | *w = MAX(1, *w); 422 | *h = MAX(1, *h); 423 | if(interact) { 424 | if(*x > sw) 425 | *x = sw - WIDTH(c); 426 | if(*y > sh) 427 | *y = sh - HEIGHT(c); 428 | if(*x + *w + 2 * c->bw < 0) 429 | *x = 0; 430 | if(*y + *h + 2 * c->bw < 0) 431 | *y = 0; 432 | } 433 | else { 434 | if(*x >= m->wx + m->ww) 435 | *x = m->wx + m->ww - WIDTH(c); 436 | if(*y >= m->wy + m->wh) 437 | *y = m->wy + m->wh - HEIGHT(c); 438 | if(*x + *w + 2 * c->bw <= m->wx) 439 | *x = m->wx; 440 | if(*y + *h + 2 * c->bw <= m->wy) 441 | *y = m->wy; 442 | } 443 | if(*h < bh) 444 | *h = bh; 445 | if(*w < bh) 446 | *w = bh; 447 | if(resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { 448 | /* see last two sentences in ICCCM 4.1.2.3 */ 449 | baseismin = c->basew == c->minw && c->baseh == c->minh; 450 | if(!baseismin) { /* temporarily remove base dimensions */ 451 | *w -= c->basew; 452 | *h -= c->baseh; 453 | } 454 | /* adjust for aspect limits */ 455 | if(c->mina > 0 && c->maxa > 0) { 456 | if(c->maxa < (float)*w / *h) 457 | *w = *h * c->maxa + 0.5; 458 | else if(c->mina < (float)*h / *w) 459 | *h = *w * c->mina + 0.5; 460 | } 461 | if(baseismin) { /* increment calculation requires this */ 462 | *w -= c->basew; 463 | *h -= c->baseh; 464 | } 465 | /* adjust for increment value */ 466 | if(c->incw) 467 | *w -= *w % c->incw; 468 | if(c->inch) 469 | *h -= *h % c->inch; 470 | /* restore base dimensions */ 471 | *w = MAX(*w + c->basew, c->minw); 472 | *h = MAX(*h + c->baseh, c->minh); 473 | if(c->maxw) 474 | *w = MIN(*w, c->maxw); 475 | if(c->maxh) 476 | *h = MIN(*h, c->maxh); 477 | } 478 | return *x != c->x || *y != c->y || *w != c->w || *h != c->h; 479 | } 480 | 481 | void 482 | arrange(Monitor *m) { 483 | if(m) { 484 | adjustborders(m); 485 | showhide(m->stack); 486 | } else for(m = mons; m; m = m->next) { 487 | adjustborders(m); 488 | showhide(m->stack); 489 | } 490 | if(m) { 491 | arrangemon(m); 492 | restack(m); 493 | } else for(m = mons; m; m = m->next) 494 | arrangemon(m); 495 | } 496 | 497 | void 498 | arrangemon(Monitor *m) { 499 | strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); 500 | if(m->lt[m->sellt]->arrange) 501 | m->lt[m->sellt]->arrange(m); 502 | } 503 | 504 | void 505 | attach(Client *c) { 506 | c->next = c->mon->clients; 507 | c->mon->clients = c; 508 | } 509 | 510 | void 511 | attachaside(Client *c) { 512 | Client *at = nexttiled(c->mon->clients); 513 | if(c->mon->sel == NULL || c->mon->sel->isfloating || !at) { 514 | attach(c); 515 | return; 516 | } 517 | c->next = at->next; 518 | at->next = c; 519 | } 520 | 521 | void 522 | attachstack(Client *c) { 523 | c->snext = c->mon->stack; 524 | c->mon->stack = c; 525 | } 526 | 527 | void 528 | buttonpress(XEvent *e) { 529 | unsigned int i, x, click; 530 | Arg arg = {0}; 531 | Client *c; 532 | Monitor *m; 533 | XButtonPressedEvent *ev = &e->xbutton; 534 | 535 | click = ClkRootWin; 536 | /* focus monitor if necessary */ 537 | if((m = wintomon(ev->window)) && m != selmon) { 538 | unfocus(selmon->sel, True); 539 | selmon = m; 540 | focus(NULL); 541 | } 542 | if(ev->window == selmon->barwin) { 543 | i = x = 0; 544 | do 545 | x += TEXTW(tags[i].name); 546 | while(ev->x >= x && ++i < LENGTH(tags)); 547 | if(i < LENGTH(tags)) { 548 | click = ClkTagBar; 549 | arg.ui = 1 << i; 550 | } 551 | else if(ev->x < x + blw) 552 | click = ClkLtSymbol; 553 | else 554 | click = ClkStatusText; 555 | } 556 | else if((c = wintoclient(ev->window))) { 557 | focus(c); 558 | click = ClkClientWin; 559 | } 560 | for(i = 0; i < LENGTH(buttons); i++) 561 | if(click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button 562 | && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) 563 | buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); 564 | } 565 | 566 | void 567 | checkotherwm(void) { 568 | xerrorxlib = XSetErrorHandler(xerrorstart); 569 | /* this causes an error if some other window manager is running */ 570 | XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); 571 | XSync(dpy, False); 572 | XSetErrorHandler(xerror); 573 | XSync(dpy, False); 574 | } 575 | 576 | void 577 | cleanup(void) { 578 | Arg a = {.ui = ~0}; 579 | Layout foo = { "", False, NULL }; 580 | Monitor *m; 581 | 582 | view(&a); 583 | selmon->lt[selmon->sellt] = &foo; 584 | for(m = mons; m; m = m->next) 585 | while(m->stack) 586 | unmanage(m->stack, False); 587 | XUngrabKey(dpy, AnyKey, AnyModifier, root); 588 | while(mons) 589 | cleanupmon(mons); 590 | if(showsystray) { 591 | XUnmapWindow(dpy, systray->win); 592 | free(systray); 593 | } 594 | drw_cur_free(drw, cursor[CurNormal]); 595 | drw_cur_free(drw, cursor[CurResize]); 596 | drw_cur_free(drw, cursor[CurMove]); 597 | drw_clr_free(scheme[SchemeNorm].border); 598 | drw_clr_free(scheme[SchemeNorm].bg); 599 | drw_clr_free(scheme[SchemeNorm].fg); 600 | drw_clr_free(scheme[SchemeSel].border); 601 | drw_clr_free(scheme[SchemeSel].bg); 602 | drw_clr_free(scheme[SchemeSel].fg); 603 | drw_free(drw); 604 | XSync(dpy, False); 605 | XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); 606 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); 607 | close(fifofd); 608 | } 609 | 610 | void 611 | cleanupmon(Monitor *mon) { 612 | Monitor *m; 613 | 614 | if(mon == mons) 615 | mons = mons->next; 616 | else { 617 | for(m = mons; m && m->next != mon; m = m->next); 618 | m->next = mon->next; 619 | } 620 | XUnmapWindow(dpy, mon->barwin); 621 | XDestroyWindow(dpy, mon->barwin); 622 | free(mon->mfacts); 623 | free(mon->nmasters); 624 | free(mon->lts); 625 | free(mon); 626 | } 627 | 628 | void 629 | clearurgent(Client *c) { 630 | XWMHints *wmh; 631 | 632 | c->isurgent = False; 633 | if(!(wmh = XGetWMHints(dpy, c->win))) 634 | return; 635 | wmh->flags &= ~XUrgencyHint; 636 | XSetWMHints(dpy, c->win, wmh); 637 | XFree(wmh); 638 | } 639 | 640 | void 641 | clientmessage(XEvent *e) { 642 | XWindowAttributes wa; 643 | XSetWindowAttributes swa; 644 | XClientMessageEvent *cme = &e->xclient; 645 | Client *c = wintoclient(cme->window); 646 | 647 | if(showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) { 648 | /* add systray icons */ 649 | if(cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { 650 | if(!(c = (Client *)calloc(1, sizeof(Client)))) 651 | die("fatal: could not malloc() %u bytes\n", sizeof(Client)); 652 | c->win = cme->data.l[2]; 653 | c->mon = selmon; 654 | c->next = systray->icons; 655 | systray->icons = c; 656 | XGetWindowAttributes(dpy, c->win, &wa); 657 | c->x = c->oldx = c->y = c->oldy = 0; 658 | c->w = c->oldw = wa.width; 659 | c->h = c->oldh = wa.height; 660 | c->oldbw = wa.border_width; 661 | c->bw = 0; 662 | c->isfloating = True; 663 | /* reuse tags field as mapped status */ 664 | c->tags = 1; 665 | updatesizehints(c); 666 | updatesystrayicongeom(c, wa.width, wa.height); 667 | XAddToSaveSet(dpy, c->win); 668 | XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask); 669 | XReparentWindow(dpy, c->win, systray->win, 0, 0); 670 | /* use parents background pixmap */ 671 | swa.background_pixmap = ParentRelative; 672 | XChangeWindowAttributes(dpy, c->win, CWBackPixmap, &swa); 673 | sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 674 | /* FIXME not sure if I have to send these events, too */ 675 | sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 676 | sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 677 | sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 678 | resizebarwin(selmon); 679 | updatesystray(); 680 | setclientstate(c, NormalState); 681 | } 682 | return; 683 | } 684 | 685 | if(!c) 686 | return; 687 | if(cme->message_type == netatom[NetWMState]) { 688 | if(cme->data.l[1] == netatom[NetWMFullscreen] || cme->data.l[2] == netatom[NetWMFullscreen]) 689 | setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ 690 | || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); 691 | } 692 | else if(cme->message_type == netatom[NetActiveWindow]) { 693 | if(!ISVISIBLE(c)) { 694 | c->mon->seltags ^= 1; 695 | c->mon->tagset[c->mon->seltags] = c->tags; 696 | } 697 | pop(c); 698 | } 699 | } 700 | 701 | void 702 | configure(Client *c) { 703 | XConfigureEvent ce; 704 | 705 | ce.type = ConfigureNotify; 706 | ce.display = dpy; 707 | ce.event = c->win; 708 | ce.window = c->win; 709 | ce.x = c->x; 710 | ce.y = c->y; 711 | ce.width = c->w; 712 | ce.height = c->h; 713 | ce.border_width = c->bw; 714 | ce.above = None; 715 | ce.override_redirect = False; 716 | XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); 717 | } 718 | 719 | void 720 | configurenotify(XEvent *e) { 721 | Monitor *m; 722 | XConfigureEvent *ev = &e->xconfigure; 723 | Bool dirty; 724 | 725 | // TODO: updategeom handling sucks, needs to be simplified 726 | if(ev->window == root) { 727 | dirty = (sw != ev->width || sh != ev->height); 728 | sw = ev->width; 729 | sh = ev->height; 730 | if(updategeom() || dirty) { 731 | drw_resize(drw, sw, bh); 732 | updatebars(); 733 | for(m = mons; m; m = m->next) 734 | resizebarwin(m); 735 | focus(NULL); 736 | arrange(NULL); 737 | } 738 | } 739 | } 740 | 741 | void 742 | configurerequest(XEvent *e) { 743 | Client *c; 744 | Monitor *m; 745 | XConfigureRequestEvent *ev = &e->xconfigurerequest; 746 | XWindowChanges wc; 747 | 748 | if((c = wintoclient(ev->window))) { 749 | if(ev->value_mask & CWBorderWidth) 750 | c->bw = ev->border_width; 751 | else if(c->isfloating || !selmon->lt[selmon->sellt]->arrange) { 752 | m = c->mon; 753 | if(ev->value_mask & CWX) { 754 | c->oldx = c->x; 755 | c->x = m->mx + ev->x; 756 | } 757 | if(ev->value_mask & CWY) { 758 | c->oldy = c->y; 759 | c->y = m->my + ev->y; 760 | } 761 | if(ev->value_mask & CWWidth) { 762 | c->oldw = c->w; 763 | c->w = ev->width; 764 | } 765 | if(ev->value_mask & CWHeight) { 766 | c->oldh = c->h; 767 | c->h = ev->height; 768 | } 769 | if((c->x + c->w) > m->mx + m->mw && c->isfloating) 770 | c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ 771 | if((c->y + c->h) > m->my + m->mh && c->isfloating) 772 | c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ 773 | if((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) 774 | configure(c); 775 | if(ISVISIBLE(c)) 776 | XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); 777 | } 778 | else 779 | configure(c); 780 | } 781 | else { 782 | wc.x = ev->x; 783 | wc.y = ev->y; 784 | wc.width = ev->width; 785 | wc.height = ev->height; 786 | wc.border_width = ev->border_width; 787 | wc.sibling = ev->above; 788 | wc.stack_mode = ev->detail; 789 | XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); 790 | } 791 | XSync(dpy, False); 792 | } 793 | 794 | Monitor * 795 | createmon(void) { 796 | Monitor *m; 797 | int i, numtags = LENGTH(tags) + 1; 798 | 799 | if(!(m = (Monitor *)calloc(1, sizeof(Monitor)))) 800 | die("fatal: could not malloc() %u bytes\n", sizeof(Monitor)); 801 | if(!(m->mfacts = calloc(numtags, sizeof(double)))) 802 | die("fatal: could not malloc() %u bytes\n", sizeof(double) * numtags); 803 | if(!(m->nmasters = calloc(numtags, sizeof(int)))) 804 | die("fatal: could not malloc() %u bytes\n", sizeof(int) * numtags); 805 | if(!(m->lts = calloc(numtags, sizeof(Layout *)))) 806 | die("fatal: could not malloc() %u bytes\n", sizeof(Layout *) * numtags); 807 | m->tagset[0] = m->tagset[1] = 1; 808 | m->mfacts[0] = mfact; 809 | m->nmasters[0] = nmaster; 810 | m->lts[0] = &layouts[0]; 811 | m->showbar = showbar; 812 | m->topbar = topbar; 813 | m->curtag = m->prevtag = 1; 814 | for(i = 1; i < numtags; i++) { 815 | m->mfacts[i] = tags[i - 1].mfact < 0 ? mfact : tags[i - 1].mfact; 816 | m->nmasters[i] = tags[i - 1].nmaster < 0 ? nmaster : tags[i - 1].nmaster; 817 | m->lts[i] = tags[i - 1].layout; 818 | } 819 | m->lt[0] = m->lts[m->curtag]; 820 | m->lt[1] = &layouts[1 % LENGTH(layouts)]; 821 | strncpy(m->ltsymbol, m->lt[0]->symbol, sizeof m->ltsymbol); 822 | return m; 823 | } 824 | 825 | void 826 | destroynotify(XEvent *e) { 827 | Client *c; 828 | XDestroyWindowEvent *ev = &e->xdestroywindow; 829 | 830 | if((c = wintoclient(ev->window))) 831 | unmanage(c, True); 832 | else if((c = wintosystrayicon(ev->window))) { 833 | removesystrayicon(c); 834 | resizebarwin(selmon); 835 | updatesystray(); 836 | } 837 | } 838 | 839 | void 840 | detach(Client *c) { 841 | Client **tc; 842 | 843 | for(tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); 844 | *tc = c->next; 845 | } 846 | 847 | void 848 | detachstack(Client *c) { 849 | Client **tc, *t; 850 | 851 | for(tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); 852 | *tc = c->snext; 853 | 854 | if(c == c->mon->sel) { 855 | for(t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); 856 | c->mon->sel = t; 857 | } 858 | } 859 | 860 | Monitor * 861 | dirtomon(int dir) { 862 | Monitor *m = NULL; 863 | 864 | if(dir > 0) { 865 | if(!(m = selmon->next)) 866 | m = mons; 867 | } 868 | else if(selmon == mons) 869 | for(m = mons; m->next; m = m->next); 870 | else 871 | for(m = mons; m->next != selmon; m = m->next); 872 | return m; 873 | } 874 | 875 | Bool 876 | evpredicate() 877 | { 878 | return True; 879 | } 880 | 881 | void 882 | dispatchcmd(void) 883 | { 884 | int i; 885 | char buf[BUFSIZ]; 886 | ssize_t n; 887 | 888 | n = read(fifofd, buf, sizeof(buf) - 1); 889 | if (n == -1) 890 | die("Failed to read() from DWM fifo %s:", dwmfifo); 891 | buf[n] = '\0'; 892 | for (i = 0; i < LENGTH(commands); i++) { 893 | if (strcmp(commands[i].name, buf) == 0) { 894 | commands[i].func(&commands[i].arg); 895 | break; 896 | } 897 | } 898 | } 899 | 900 | void 901 | drawbar(Monitor *m) { 902 | int x, xx, w; 903 | unsigned int i, occ = 0, urg = 0, a=0, s=0; 904 | char posbuf[10]; 905 | Client *c; 906 | 907 | resizebarwin(m); 908 | for(c = m->clients; c; c = c->next) { 909 | occ |= c->tags; 910 | if(c->isurgent) 911 | urg |= c->tags; 912 | } 913 | x = 0; 914 | 915 | for(i = 0; i < LENGTH(tags); i++) { 916 | w = TEXTW(tags[i].name); 917 | drw_setscheme(drw, &scheme[(m->tagset[m->seltags] & 1 << i) ? 14 : (urg & 1 << i ? 15 : (occ & 1 << i ? 16 : 13))]); 918 | drw_text(drw, x, 0, w, bh, tags[i].name, 0); 919 | XSetForeground(drw->dpy, drw->gc, m == selmon && selmon->sel && selmon->sel->tags & 1 << i ? drw->scheme->fg->pix:drw->scheme->border->pix); 920 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, bh - taglinepx, w, taglinepx); 921 | x += w; 922 | } 923 | w = blw = TEXTW(m->ltsymbol); 924 | drw_setscheme(drw, &scheme[9]); 925 | drw_text(drw, x, 0, w, bh, m->ltsymbol, 0); 926 | if(m->lt[m->sellt]->arrange == monocle) { 927 | for (c=nexttiled(m->clients), a=0, s=0; c; c=nexttiled(c->next), a++) 928 | if(c== m->stack) 929 | s=a; 930 | if(!s && a) 931 | s=a; 932 | snprintf(posbuf, LENGTH(posbuf), "[%d/%d]", s, a); 933 | w=TEXTW(posbuf); 934 | drw_text(drw, x, 0, w, bh, posbuf, 0); 935 | xx = x + w; 936 | } 937 | x += w; 938 | xx = x; 939 | if(m == selmon) { /* status is only drawn on selected monitor */ 940 | w = TEXTW(stext) - tagpadding; 941 | x = m->ww - w; 942 | if(systray) { 943 | x -= getsystraywidth(); 944 | } 945 | if(x < xx) { 946 | x = xx; 947 | w = m->ww - xx; 948 | } 949 | drw_colored_text(drw, scheme, NUMCOLORS, x, 0, w, bh, stext); 950 | } 951 | else 952 | x = m->ww; 953 | if((w = x - xx) > bh) { 954 | x = xx; 955 | drw_setscheme(drw, &scheme[9]); 956 | drw_text(drw, x, 0, w, bh, NULL, 0); 957 | } 958 | drw_map(drw, m->barwin, 0, 0, m->ww, bh); 959 | } 960 | 961 | void 962 | drawbars(void) { 963 | Monitor *m; 964 | 965 | for(m = mons; m; m = m->next) 966 | drawbar(m); 967 | updatesystray(); 968 | } 969 | 970 | void 971 | enternotify(XEvent *e) { 972 | Client *c; 973 | Monitor *m; 974 | XCrossingEvent *ev = &e->xcrossing; 975 | 976 | if((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) 977 | return; 978 | c = wintoclient(ev->window); 979 | m = c ? c->mon : wintomon(ev->window); 980 | if(m != selmon) { 981 | unfocus(selmon->sel, True); 982 | selmon = m; 983 | } 984 | else if(!c || c == selmon->sel) 985 | return; 986 | focus(c); 987 | } 988 | 989 | void 990 | expose(XEvent *e) { 991 | Monitor *m; 992 | XExposeEvent *ev = &e->xexpose; 993 | 994 | if(ev->count == 0 && (m = wintomon(ev->window))) 995 | drawbar(m); 996 | } 997 | 998 | void 999 | focus(Client *c) { 1000 | if(!c || !ISVISIBLE(c)) 1001 | for(c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); 1002 | /* was if(selmon->sel) */ 1003 | if(selmon->sel && selmon->sel != c) 1004 | unfocus(selmon->sel, False); 1005 | if(c) { 1006 | if(c->mon != selmon) 1007 | selmon = c->mon; 1008 | if(c->isurgent) 1009 | clearurgent(c); 1010 | detachstack(c); 1011 | attachstack(c); 1012 | grabbuttons(c, True); 1013 | XSetWindowBorder(dpy, c->win, scheme[1].border->pix); 1014 | setfocus(c); 1015 | } 1016 | else { 1017 | XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); 1018 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); 1019 | } 1020 | selmon->sel = c; 1021 | drawbars(); 1022 | } 1023 | 1024 | void 1025 | focusin(XEvent *e) { /* there are some broken focus acquiring clients */ 1026 | XFocusChangeEvent *ev = &e->xfocus; 1027 | 1028 | if(selmon->sel && ev->window != selmon->sel->win) 1029 | setfocus(selmon->sel); 1030 | } 1031 | 1032 | void 1033 | focusmon(const Arg *arg) { 1034 | Monitor *m; 1035 | 1036 | if(!mons->next) 1037 | return; 1038 | if((m = dirtomon(arg->i)) == selmon) 1039 | return; 1040 | unfocus(selmon->sel, False); /* s/True/False/ fixes input focus issues 1041 | in gedit and anjuta */ 1042 | selmon = m; 1043 | focus(NULL); 1044 | } 1045 | 1046 | void 1047 | focusstack(const Arg *arg) { 1048 | int i = stackpos(arg); 1049 | Client *c, *p; 1050 | 1051 | if(i < 0) 1052 | return; 1053 | 1054 | for(p = NULL, c = selmon->clients; c && (i || !ISVISIBLE(c)); 1055 | i -= ISVISIBLE(c) ? 1 : 0, p = c, c = c->next); 1056 | focus(c ? c : p); 1057 | restack(selmon); 1058 | } 1059 | 1060 | Atom 1061 | getatomprop(Client *c, Atom prop) { 1062 | int di; 1063 | unsigned long dl; 1064 | unsigned char *p = NULL; 1065 | Atom da, atom = None; 1066 | /* FIXME getatomprop should return the number of items and a pointer to 1067 | * the stored data instead of this workaround */ 1068 | Atom req = XA_ATOM; 1069 | if(prop == xatom[XembedInfo]) 1070 | req = xatom[XembedInfo]; 1071 | 1072 | if(XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, 1073 | &da, &di, &dl, &dl, &p) == Success && p) { 1074 | atom = *(Atom *)p; 1075 | if(da == xatom[XembedInfo] && dl == 2) 1076 | atom = ((Atom *)p)[1]; 1077 | XFree(p); 1078 | } 1079 | return atom; 1080 | } 1081 | 1082 | Bool 1083 | getrootptr(int *x, int *y) { 1084 | int di; 1085 | unsigned int dui; 1086 | Window dummy; 1087 | 1088 | return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); 1089 | } 1090 | 1091 | long 1092 | getstate(Window w) { 1093 | int format; 1094 | long result = -1; 1095 | unsigned char *p = NULL; 1096 | unsigned long n, extra; 1097 | Atom real; 1098 | 1099 | if(XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], 1100 | &real, &format, &n, &extra, (unsigned char **)&p) != Success) 1101 | return -1; 1102 | if(n != 0) 1103 | result = *p; 1104 | XFree(p); 1105 | return result; 1106 | } 1107 | 1108 | Bool 1109 | gettextprop(Window w, Atom atom, char *text, unsigned int size) { 1110 | char **list = NULL; 1111 | int n; 1112 | XTextProperty name; 1113 | 1114 | if(!text || size == 0) 1115 | return False; 1116 | text[0] = '\0'; 1117 | XGetTextProperty(dpy, w, &name, atom); 1118 | if(!name.nitems) 1119 | return False; 1120 | if(name.encoding == XA_STRING) 1121 | strncpy(text, (char *)name.value, size - 1); 1122 | else { 1123 | if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { 1124 | strncpy(text, *list, size - 1); 1125 | XFreeStringList(list); 1126 | } 1127 | } 1128 | text[size - 1] = '\0'; 1129 | XFree(name.value); 1130 | return True; 1131 | } 1132 | 1133 | void 1134 | grabbuttons(Client *c, Bool focused) { 1135 | updatenumlockmask(); 1136 | { 1137 | unsigned int i, j; 1138 | unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; 1139 | XUngrabButton(dpy, AnyButton, AnyModifier, c->win); 1140 | if(focused) { 1141 | for(i = 0; i < LENGTH(buttons); i++) 1142 | if(buttons[i].click == ClkClientWin) 1143 | for(j = 0; j < LENGTH(modifiers); j++) 1144 | XGrabButton(dpy, buttons[i].button, 1145 | buttons[i].mask | modifiers[j], 1146 | c->win, False, BUTTONMASK, 1147 | GrabModeAsync, GrabModeSync, None, None); 1148 | } 1149 | else 1150 | XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, 1151 | BUTTONMASK, GrabModeAsync, GrabModeSync, None, None); 1152 | } 1153 | } 1154 | 1155 | void 1156 | grabkeys(void) { 1157 | updatenumlockmask(); 1158 | { 1159 | unsigned int i, j; 1160 | unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; 1161 | KeyCode code; 1162 | 1163 | XUngrabKey(dpy, AnyKey, AnyModifier, root); 1164 | for(i = 0; i < LENGTH(keys); i++) 1165 | if((code = XKeysymToKeycode(dpy, keys[i].keysym))) 1166 | for(j = 0; j < LENGTH(modifiers); j++) 1167 | XGrabKey(dpy, code, keys[i].mod | modifiers[j], root, 1168 | True, GrabModeAsync, GrabModeAsync); 1169 | } 1170 | } 1171 | 1172 | void 1173 | incnmaster(const Arg *arg) { 1174 | selmon->nmasters[selmon->curtag] = MAX(selmon->nmasters[selmon->curtag] + arg->i, 0); 1175 | arrange(selmon); 1176 | } 1177 | 1178 | #ifdef XINERAMA 1179 | static Bool 1180 | isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) { 1181 | while(n--) 1182 | if(unique[n].x_org == info->x_org && unique[n].y_org == info->y_org 1183 | && unique[n].width == info->width && unique[n].height == info->height) 1184 | return False; 1185 | return True; 1186 | } 1187 | #endif /* XINERAMA */ 1188 | 1189 | void 1190 | keypress(XEvent *e) { 1191 | unsigned int i; 1192 | KeySym keysym; 1193 | XKeyEvent *ev; 1194 | 1195 | ev = &e->xkey; 1196 | keysym = XkbKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0, 0); 1197 | for(i = 0; i < LENGTH(keys); i++) 1198 | if(keysym == keys[i].keysym 1199 | && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) 1200 | && keys[i].func) 1201 | keys[i].func(&(keys[i].arg)); 1202 | } 1203 | 1204 | void 1205 | killclient(const Arg *arg) { 1206 | if(!selmon->sel) 1207 | return; 1208 | if(!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) { 1209 | XGrabServer(dpy); 1210 | XSetErrorHandler(xerrordummy); 1211 | XSetCloseDownMode(dpy, DestroyAll); 1212 | XKillClient(dpy, selmon->sel->win); 1213 | XSync(dpy, False); 1214 | XSetErrorHandler(xerror); 1215 | XUngrabServer(dpy); 1216 | } 1217 | } 1218 | 1219 | void 1220 | manage(Window w, XWindowAttributes *wa) { 1221 | Client *c, *t = NULL; 1222 | Window trans = None; 1223 | XWindowChanges wc; 1224 | 1225 | if(!(c = calloc(1, sizeof(Client)))) 1226 | die("fatal: could not malloc() %u bytes\n", sizeof(Client)); 1227 | c->win = w; 1228 | updatetitle(c); 1229 | if(XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { 1230 | c->mon = t->mon; 1231 | c->tags = t->tags; 1232 | } 1233 | else { 1234 | c->mon = selmon; 1235 | applyrules(c); 1236 | } 1237 | /* geometry */ 1238 | c->x = c->oldx = wa->x; 1239 | c->y = c->oldy = wa->y; 1240 | c->w = c->oldw = wa->width; 1241 | c->h = c->oldh = wa->height; 1242 | c->oldbw = wa->border_width; 1243 | 1244 | if(c->x + WIDTH(c) > c->mon->mx + c->mon->mw) 1245 | c->x = c->mon->mx + c->mon->mw - WIDTH(c); 1246 | if(c->y + HEIGHT(c) > c->mon->my + c->mon->mh) 1247 | c->y = c->mon->my + c->mon->mh - HEIGHT(c); 1248 | c->x = MAX(c->x, c->mon->mx); 1249 | /* only fix client y-offset, if the client center might cover the bar */ 1250 | c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) 1251 | && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); 1252 | 1253 | updatewindowtype(c); 1254 | if (c->isfloating) { 1255 | c->bw = c->isfullscreen ? 0 : borderpx; 1256 | } else { 1257 | c->bw = 0; 1258 | for(t = c->mon->clients; t; t = c->next) { 1259 | if (!t->isfloating && c != t && c->tags & t->tags) { 1260 | c->bw = borderpx; 1261 | break; 1262 | } 1263 | } 1264 | adjustborders(c->mon); 1265 | } 1266 | 1267 | wc.border_width = c->bw; 1268 | XConfigureWindow(dpy, w, CWBorderWidth, &wc); 1269 | XSetWindowBorder(dpy, w, scheme[0].border->pix); 1270 | configure(c); /* propagates border_width, if size doesn't change */ 1271 | updatewindowtype(c); 1272 | updatesizehints(c); 1273 | updatewmhints(c); 1274 | XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); 1275 | grabbuttons(c, False); 1276 | if(!c->isfloating) 1277 | c->isfloating = c->oldstate = trans != None || c->isfixed; 1278 | if(c->isfloating) 1279 | XRaiseWindow(dpy, c->win); 1280 | attachaside(c); 1281 | attachstack(c); 1282 | XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, 1283 | (unsigned char *) &(c->win), 1); 1284 | XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ 1285 | setclientstate(c, NormalState); 1286 | if (c->mon == selmon) 1287 | unfocus(selmon->sel, False); 1288 | c->mon->sel = c; 1289 | arrange(c->mon); 1290 | XMapWindow(dpy, c->win); 1291 | focus(NULL); 1292 | } 1293 | 1294 | void 1295 | mappingnotify(XEvent *e) { 1296 | XMappingEvent *ev = &e->xmapping; 1297 | 1298 | XRefreshKeyboardMapping(ev); 1299 | if(ev->request == MappingKeyboard) 1300 | grabkeys(); 1301 | } 1302 | 1303 | void 1304 | maprequest(XEvent *e) { 1305 | static XWindowAttributes wa; 1306 | XMapRequestEvent *ev = &e->xmaprequest; 1307 | Client *i; 1308 | 1309 | if((i = wintosystrayicon(ev->window))) { 1310 | sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); 1311 | resizebarwin(selmon); 1312 | updatesystray(); 1313 | } 1314 | if(!XGetWindowAttributes(dpy, ev->window, &wa)) 1315 | return; 1316 | if(wa.override_redirect) 1317 | return; 1318 | if(!wintoclient(ev->window)) 1319 | manage(ev->window, &wa); 1320 | } 1321 | 1322 | void 1323 | monocle(Monitor *m) { 1324 | unsigned int n = 0; 1325 | Client *c; 1326 | 1327 | for(c = m->clients; c; c = c->next) 1328 | if(ISVISIBLE(c)) 1329 | n++; 1330 | for(c = nexttiled(m->clients); c; c = nexttiled(c->next)) 1331 | resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, False); 1332 | } 1333 | 1334 | void 1335 | motionnotify(XEvent *e) { 1336 | static Monitor *mon = NULL; 1337 | Monitor *m; 1338 | XMotionEvent *ev = &e->xmotion; 1339 | 1340 | if(ev->window != root) 1341 | return; 1342 | if((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { 1343 | unfocus(selmon->sel, True); 1344 | selmon = m; 1345 | focus(NULL); 1346 | } 1347 | mon = m; 1348 | } 1349 | 1350 | void 1351 | movemouse(const Arg *arg) { 1352 | int x, y, ocx, ocy, nx, ny; 1353 | Client *c; 1354 | Monitor *m; 1355 | XEvent ev; 1356 | Time lasttime = 0; 1357 | 1358 | if(!(c = selmon->sel)) 1359 | return; 1360 | if(c->isfullscreen) /* no support moving fullscreen windows by mouse */ 1361 | return; 1362 | restack(selmon); 1363 | ocx = c->x; 1364 | ocy = c->y; 1365 | if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, 1366 | None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) 1367 | return; 1368 | if(!getrootptr(&x, &y)) 1369 | return; 1370 | do { 1371 | XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); 1372 | switch(ev.type) { 1373 | case ConfigureRequest: 1374 | case Expose: 1375 | case MapRequest: 1376 | handler[ev.type](&ev); 1377 | break; 1378 | case MotionNotify: 1379 | if ((ev.xmotion.time - lasttime) <= (1000 / 60)) 1380 | continue; 1381 | lasttime = ev.xmotion.time; 1382 | 1383 | nx = ocx + (ev.xmotion.x - x); 1384 | ny = ocy + (ev.xmotion.y - y); 1385 | if(nx >= selmon->wx && nx <= selmon->wx + selmon->ww 1386 | && ny >= selmon->wy && ny <= selmon->wy + selmon->wh) { 1387 | if(abs(selmon->wx - nx) < snap) 1388 | nx = selmon->wx; 1389 | else if(abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) 1390 | nx = selmon->wx + selmon->ww - WIDTH(c); 1391 | if(abs(selmon->wy - ny) < snap) 1392 | ny = selmon->wy; 1393 | else if(abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) 1394 | ny = selmon->wy + selmon->wh - HEIGHT(c); 1395 | if(!c->isfloating && selmon->lt[selmon->sellt]->arrange 1396 | && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) 1397 | togglefloating(NULL); 1398 | } 1399 | if(!selmon->lt[selmon->sellt]->arrange || c->isfloating) 1400 | resize(c, nx, ny, c->w, c->h, True); 1401 | break; 1402 | } 1403 | } while(ev.type != ButtonRelease); 1404 | XUngrabPointer(dpy, CurrentTime); 1405 | if((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { 1406 | sendmon(c, m); 1407 | selmon = m; 1408 | focus(NULL); 1409 | } 1410 | } 1411 | 1412 | Client * 1413 | nexttiled(Client *c) { 1414 | for(; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); 1415 | return c; 1416 | } 1417 | 1418 | void 1419 | pop(Client *c) { 1420 | detach(c); 1421 | attach(c); 1422 | focus(c); 1423 | arrange(c->mon); 1424 | } 1425 | 1426 | void 1427 | propertynotify(XEvent *e) { 1428 | Client *c; 1429 | Window trans; 1430 | XPropertyEvent *ev = &e->xproperty; 1431 | 1432 | if((c = wintosystrayicon(ev->window))) { 1433 | if(ev->atom == XA_WM_NORMAL_HINTS) { 1434 | updatesizehints(c); 1435 | updatesystrayicongeom(c, c->w, c->h); 1436 | } 1437 | else 1438 | updatesystrayiconstate(c, ev); 1439 | resizebarwin(selmon); 1440 | updatesystray(); 1441 | } 1442 | if((ev->window == root) && (ev->atom == XA_WM_NAME)) 1443 | updatestatus(); 1444 | else if(ev->state == PropertyDelete) 1445 | return; /* ignore */ 1446 | else if((c = wintoclient(ev->window))) { 1447 | switch(ev->atom) { 1448 | default: break; 1449 | case XA_WM_TRANSIENT_FOR: 1450 | if(!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && 1451 | (c->isfloating = (wintoclient(trans)) != NULL)) 1452 | arrange(c->mon); 1453 | break; 1454 | case XA_WM_NORMAL_HINTS: 1455 | updatesizehints(c); 1456 | break; 1457 | case XA_WM_HINTS: 1458 | updatewmhints(c); 1459 | drawbars(); 1460 | break; 1461 | } 1462 | if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) 1463 | updatetitle(c); 1464 | if(ev->atom == netatom[NetWMWindowType]) 1465 | updatewindowtype(c); 1466 | } 1467 | } 1468 | 1469 | void 1470 | pushstack(const Arg *arg) { 1471 | int i = stackpos(arg); 1472 | Client *sel = selmon->sel, *c, *p; 1473 | 1474 | if(i < 0) 1475 | return; 1476 | else if(i == 0) { 1477 | detach(sel); 1478 | attach(sel); 1479 | } 1480 | else { 1481 | for(p = NULL, c = selmon->clients; c; p = c, c = c->next) 1482 | if(!(i -= (ISVISIBLE(c) && c != sel))) 1483 | break; 1484 | c = c ? c : p; 1485 | detach(sel); 1486 | sel->next = c->next; 1487 | c->next = sel; 1488 | } 1489 | arrange(selmon); 1490 | } 1491 | 1492 | void 1493 | quit(const Arg *arg) { 1494 | running = False; 1495 | } 1496 | 1497 | Monitor * 1498 | recttomon(int x, int y, int w, int h) { 1499 | Monitor *m, *r = selmon; 1500 | int a, area = 0; 1501 | 1502 | for(m = mons; m; m = m->next) 1503 | if((a = INTERSECT(x, y, w, h, m)) > area) { 1504 | area = a; 1505 | r = m; 1506 | } 1507 | return r; 1508 | } 1509 | 1510 | void 1511 | resize(Client *c, int x, int y, int w, int h, Bool interact) { 1512 | if(applysizehints(c, &x, &y, &w, &h, interact)) 1513 | resizeclient(c, x, y, w, h); 1514 | } 1515 | 1516 | void 1517 | resizeclient(Client *c, int x, int y, int w, int h) { 1518 | XWindowChanges wc; 1519 | 1520 | gap = c->isfloating ? 0 : c->mon->lt[c->mon->sellt]->addgaps ? gappx : 0; 1521 | c->oldx = c->x; c->x = wc.x = x + gap; 1522 | c->oldy = c->y; c->y = wc.y = y + gap; 1523 | c->oldw = c->w; c->w = wc.width = w - (gap ? (x + w + (c->bw * 2) == c->mon->mx + c->mon->mw ? 2 : 1) * gap : 0); 1524 | c->oldh = c->h; c->h = wc.height = h - (gap ? (y + h + (c->bw * 2) == c->mon->my + c->mon->mh ? 2 : 1) * gap : 0); 1525 | wc.border_width = c->bw; 1526 | XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); 1527 | configure(c); 1528 | XSync(dpy, False); 1529 | } 1530 | 1531 | void 1532 | resizemouse(const Arg *arg) { 1533 | int ocx, ocy, nw, nh; 1534 | Client *c; 1535 | Monitor *m; 1536 | XEvent ev; 1537 | Time lasttime = 0; 1538 | 1539 | if(!(c = selmon->sel)) 1540 | return; 1541 | if(c->isfullscreen) /* no support resizing fullscreen windows by mouse */ 1542 | return; 1543 | restack(selmon); 1544 | ocx = c->x; 1545 | ocy = c->y; 1546 | if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, 1547 | None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) 1548 | return; 1549 | XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); 1550 | do { 1551 | XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); 1552 | switch(ev.type) { 1553 | case ConfigureRequest: 1554 | case Expose: 1555 | case MapRequest: 1556 | handler[ev.type](&ev); 1557 | break; 1558 | case MotionNotify: 1559 | if ((ev.xmotion.time - lasttime) <= (1000 / 60)) 1560 | continue; 1561 | lasttime = ev.xmotion.time; 1562 | 1563 | nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); 1564 | nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); 1565 | if(c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww 1566 | && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) 1567 | { 1568 | if(!c->isfloating && selmon->lt[selmon->sellt]->arrange 1569 | && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) 1570 | togglefloating(NULL); 1571 | } 1572 | if(!selmon->lt[selmon->sellt]->arrange || c->isfloating) 1573 | resize(c, c->x, c->y, nw, nh, True); 1574 | break; 1575 | } 1576 | } while(ev.type != ButtonRelease); 1577 | XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); 1578 | XUngrabPointer(dpy, CurrentTime); 1579 | while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); 1580 | if((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { 1581 | sendmon(c, m); 1582 | selmon = m; 1583 | focus(NULL); 1584 | } 1585 | } 1586 | 1587 | void 1588 | restack(Monitor *m) { 1589 | Client *c; 1590 | XEvent ev; 1591 | XWindowChanges wc; 1592 | 1593 | drawbar(m); 1594 | if(!m->sel) 1595 | return; 1596 | if(m->sel->isfloating || !m->lt[m->sellt]->arrange) 1597 | XRaiseWindow(dpy, m->sel->win); 1598 | if(m->lt[m->sellt]->arrange) { 1599 | wc.stack_mode = Below; 1600 | wc.sibling = m->barwin; 1601 | for(c = m->stack; c; c = c->snext) 1602 | if(!c->isfloating && ISVISIBLE(c)) { 1603 | XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); 1604 | wc.sibling = c->win; 1605 | } 1606 | } 1607 | XSync(dpy, False); 1608 | while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); 1609 | } 1610 | 1611 | void 1612 | run(void) { 1613 | XEvent ev; 1614 | /* main event loop */ 1615 | fd_set rfds; 1616 | int n; 1617 | int dpyfd, maxfd; 1618 | /* main event loop */ 1619 | XSync(dpy, False); 1620 | dpyfd = ConnectionNumber(dpy); 1621 | maxfd = fifofd; 1622 | if (dpyfd > maxfd) 1623 | maxfd = dpyfd; 1624 | maxfd++; 1625 | while (running) { 1626 | FD_ZERO(&rfds); 1627 | FD_SET(fifofd, &rfds); 1628 | FD_SET(dpyfd, &rfds); 1629 | n = select(maxfd, &rfds, NULL, NULL, NULL); 1630 | if (n > 0) { 1631 | if (FD_ISSET(fifofd, &rfds)) 1632 | dispatchcmd(); 1633 | if (FD_ISSET(dpyfd, &rfds)) 1634 | while (XCheckIfEvent(dpy, &ev, evpredicate, NULL)) 1635 | if (handler[ev.type]) 1636 | handler[ev.type](&ev); /* call handler */ 1637 | } 1638 | } 1639 | } 1640 | 1641 | void 1642 | runorraise(const Arg *arg) { 1643 | char *app = ((char **)arg->v)[4]; 1644 | Arg a = { .ui = ~0 }; 1645 | Monitor *mon; 1646 | Client *c; 1647 | XClassHint hint = { NULL, NULL }; 1648 | /* Tries to find the client */ 1649 | for (mon = mons; mon; mon = mon->next) { 1650 | for (c = mon->clients; c; c = c->next) { 1651 | XGetClassHint(dpy, c->win, &hint); 1652 | if (hint.res_class && strcmp(app, hint.res_class) == 0) { 1653 | a.ui = c->tags; 1654 | view(&a); 1655 | focus(c); 1656 | XRaiseWindow(dpy, c->win); 1657 | return; 1658 | } 1659 | } 1660 | } 1661 | /* Client not found: spawn it */ 1662 | spawn(arg); 1663 | } 1664 | 1665 | void 1666 | scan(void) { 1667 | unsigned int i, num; 1668 | Window d1, d2, *wins = NULL; 1669 | XWindowAttributes wa; 1670 | 1671 | if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { 1672 | for(i = 0; i < num; i++) { 1673 | if(!XGetWindowAttributes(dpy, wins[i], &wa) 1674 | || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) 1675 | continue; 1676 | if(wa.map_state == IsViewable || getstate(wins[i]) == IconicState) 1677 | manage(wins[i], &wa); 1678 | } 1679 | for(i = 0; i < num; i++) { /* now the transients */ 1680 | if(!XGetWindowAttributes(dpy, wins[i], &wa)) 1681 | continue; 1682 | if(XGetTransientForHint(dpy, wins[i], &d1) 1683 | && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) 1684 | manage(wins[i], &wa); 1685 | } 1686 | if(wins) 1687 | XFree(wins); 1688 | } 1689 | } 1690 | 1691 | void 1692 | self_restart(const Arg *arg) { 1693 | char* const argv[] = {(char*)dwmpath, NULL}; 1694 | 1695 | if(argv[0] == NULL) { 1696 | return; 1697 | } 1698 | 1699 | execv(argv[0], argv); 1700 | } 1701 | void 1702 | sendmon(Client *c, Monitor *m) { 1703 | if(c->mon == m) 1704 | return; 1705 | unfocus(c, True); 1706 | detach(c); 1707 | detachstack(c); 1708 | c->mon = m; 1709 | c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ 1710 | attachaside(c); 1711 | attachstack(c); 1712 | focus(NULL); 1713 | arrange(NULL); 1714 | } 1715 | 1716 | void 1717 | setclientstate(Client *c, long state) { 1718 | long data[] = { state, None }; 1719 | 1720 | XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, 1721 | PropModeReplace, (unsigned char *)data, 2); 1722 | } 1723 | 1724 | Bool 1725 | sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) { 1726 | int n; 1727 | Atom *protocols, mt; 1728 | Bool exists = False; 1729 | XEvent ev; 1730 | 1731 | if(proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { 1732 | mt = wmatom[WMProtocols]; 1733 | if(XGetWMProtocols(dpy, w, &protocols, &n)) { 1734 | while(!exists && n--) 1735 | exists = protocols[n] == proto; 1736 | XFree(protocols); 1737 | } 1738 | } 1739 | else { 1740 | exists = True; 1741 | mt = proto; 1742 | } 1743 | if(exists) { 1744 | ev.type = ClientMessage; 1745 | ev.xclient.window = w; 1746 | ev.xclient.message_type = mt; 1747 | ev.xclient.format = 32; 1748 | ev.xclient.data.l[0] = d0; 1749 | ev.xclient.data.l[1] = d1; 1750 | ev.xclient.data.l[2] = d2; 1751 | ev.xclient.data.l[3] = d3; 1752 | ev.xclient.data.l[4] = d4; 1753 | XSendEvent(dpy, w, False, mask, &ev); 1754 | } 1755 | return exists; 1756 | } 1757 | 1758 | void 1759 | setfocus(Client *c) { 1760 | if(!c->neverfocus) { 1761 | XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); 1762 | XChangeProperty(dpy, root, netatom[NetActiveWindow], 1763 | XA_WINDOW, 32, PropModeReplace, 1764 | (unsigned char *) &(c->win), 1); 1765 | } 1766 | sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); 1767 | } 1768 | 1769 | void 1770 | setfullscreen(Client *c, Bool fullscreen) { 1771 | if(fullscreen) { 1772 | XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, 1773 | PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); 1774 | c->isfullscreen = True; 1775 | c->oldstate = c->isfloating; 1776 | c->oldbw = c->bw; 1777 | c->bw = 0; 1778 | c->isfloating = True; 1779 | resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); 1780 | XRaiseWindow(dpy, c->win); 1781 | } 1782 | else { 1783 | XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, 1784 | PropModeReplace, (unsigned char*)0, 0); 1785 | c->isfullscreen = False; 1786 | c->isfloating = c->oldstate; 1787 | c->bw = c->oldbw; 1788 | c->x = c->oldx; 1789 | c->y = c->oldy; 1790 | c->w = c->oldw; 1791 | c->h = c->oldh; 1792 | resizeclient(c, c->x, c->y, c->w, c->h); 1793 | arrange(c->mon); 1794 | } 1795 | } 1796 | 1797 | void 1798 | setlayout(const Arg *arg) { 1799 | if(!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) 1800 | selmon->sellt ^= 1; 1801 | if(arg && arg->v) 1802 | selmon->lt[selmon->sellt] = selmon->lts[selmon->curtag] = (Layout *)arg->v; 1803 | strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); 1804 | if(selmon->sel) 1805 | arrange(selmon); 1806 | else 1807 | drawbar(selmon); 1808 | } 1809 | 1810 | /* arg > 1.0 will set mfact absolutly */ 1811 | void 1812 | setmfact(const Arg *arg) { 1813 | float f; 1814 | 1815 | if(!arg || !selmon->lt[selmon->sellt]->arrange) 1816 | return; 1817 | f = arg->f < 1.0 ? arg->f + selmon->mfacts[selmon->curtag] : arg->f - 1.0; 1818 | if(f < 0.1 || f > 0.9) 1819 | return; 1820 | selmon->mfacts[selmon->curtag] = f; 1821 | arrange(selmon); 1822 | } 1823 | 1824 | void 1825 | setup(void) { 1826 | XSetWindowAttributes wa; 1827 | 1828 | /* clean up any zombies immediately */ 1829 | sigchld(0); 1830 | 1831 | /* init screen */ 1832 | screen = DefaultScreen(dpy); 1833 | sw = DisplayWidth(dpy, screen); 1834 | sh = DisplayHeight(dpy, screen); 1835 | root = RootWindow(dpy, screen); 1836 | drw = drw_create(dpy, screen, root, sw, sh); 1837 | drw_load_fonts(drw, fonts, LENGTH(fonts)); 1838 | if (!drw->fontcount) 1839 | die("No fonts could be loaded.\n"); 1840 | bh = drw->fonts[0]->h + 2; 1841 | updategeom(); 1842 | /* init atoms */ 1843 | wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); 1844 | wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); 1845 | wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); 1846 | wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); 1847 | netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); 1848 | netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); 1849 | netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); 1850 | netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); 1851 | netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); 1852 | netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); 1853 | netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); 1854 | netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); 1855 | netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); 1856 | netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); 1857 | netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); 1858 | xatom[Manager] = XInternAtom(dpy, "MANAGER", False); 1859 | xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); 1860 | xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); 1861 | /* init cursors */ 1862 | cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); 1863 | cursor[CurResize] = drw_cur_create(drw, XC_sizing); 1864 | cursor[CurMove] = drw_cur_create(drw, XC_fleur); 1865 | /* init appearance */ 1866 | for(int i = 0; i < NUMCOLORS; i++){ 1867 | scheme[i].border = drw_clr_create(drw, colors[i][0]); 1868 | scheme[i].fg = drw_clr_create(drw, colors[i][1]); 1869 | scheme[i].bg = drw_clr_create(drw, colors[i][2]); 1870 | } 1871 | /* init system tray */ 1872 | updatesystray(); 1873 | /* init bars */ 1874 | updatebars(); 1875 | updatestatus(); 1876 | /* EWMH support per view */ 1877 | XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, 1878 | PropModeReplace, (unsigned char *) netatom, NetLast); 1879 | XDeleteProperty(dpy, root, netatom[NetClientList]); 1880 | /* select for events */ 1881 | wa.cursor = cursor[CurNormal]->cursor; 1882 | wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask|ButtonPressMask|PointerMotionMask 1883 | |EnterWindowMask|LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; 1884 | XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); 1885 | XSelectInput(dpy, root, wa.event_mask); 1886 | grabkeys(); 1887 | focus(NULL); 1888 | fifofd = open(dwmfifo, O_RDWR | O_NONBLOCK); 1889 | if (fifofd < 0) 1890 | die("Failed to open() DWM fifo %s:", dwmfifo); 1891 | } 1892 | 1893 | void 1894 | showhide(Client *c) { 1895 | if(!c) 1896 | return; 1897 | if(ISVISIBLE(c)) { /* show clients top down */ 1898 | XMoveWindow(dpy, c->win, c->x, c->y); 1899 | if((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) 1900 | resize(c, c->x, c->y, c->w, c->h, False); 1901 | showhide(c->snext); 1902 | } 1903 | else { /* hide clients bottom up */ 1904 | showhide(c->snext); 1905 | XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); 1906 | } 1907 | } 1908 | 1909 | void 1910 | sigchld(int unused) { 1911 | if(signal(SIGCHLD, sigchld) == SIG_ERR) 1912 | die("Can't install SIGCHLD handler"); 1913 | while(0 < waitpid(-1, NULL, WNOHANG)); 1914 | } 1915 | 1916 | void 1917 | spawn(const Arg *arg) { 1918 | if(fork() == 0) { 1919 | if(dpy) 1920 | close(ConnectionNumber(dpy)); 1921 | setsid(); 1922 | execvp(((char **)arg->v)[0], (char **)arg->v); 1923 | fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]); 1924 | perror(" failed"); 1925 | exit(EXIT_SUCCESS); 1926 | } 1927 | } 1928 | 1929 | int 1930 | stackpos(const Arg *arg) { 1931 | int n, i; 1932 | Client *c, *l; 1933 | 1934 | if(!selmon->clients) 1935 | return -1; 1936 | 1937 | if(arg->i == PREVSEL) { 1938 | for(l = selmon->stack; l && (!ISVISIBLE(l) || l == selmon->sel); l = l->snext); 1939 | if(!l) 1940 | return -1; 1941 | for(i = 0, c = selmon->clients; c != l; i += ISVISIBLE(c) ? 1 : 0, c = c->next); 1942 | return i; 1943 | } 1944 | else if(ISINC(arg->i)) { 1945 | if(!selmon->sel) 1946 | return -1; 1947 | for(i = 0, c = selmon->clients; c != selmon->sel; i += ISVISIBLE(c) ? 1 : 0, c = c->next); 1948 | for(n = i; c; n += ISVISIBLE(c) ? 1 : 0, c = c->next); 1949 | return MOD(i + GETINC(arg->i), n); 1950 | } 1951 | else if(arg->i < 0) { 1952 | for(i = 0, c = selmon->clients; c; i += ISVISIBLE(c) ? 1 : 0, c = c->next); 1953 | return MAX(i + arg->i, 0); 1954 | } 1955 | else 1956 | return arg->i; 1957 | } 1958 | 1959 | void 1960 | tag(const Arg *arg) { 1961 | if(selmon->sel && arg->ui & TAGMASK) { 1962 | selmon->sel->tags = arg->ui & TAGMASK; 1963 | focus(NULL); 1964 | arrange(selmon); 1965 | } 1966 | } 1967 | 1968 | void 1969 | tagmon(const Arg *arg) { 1970 | if(!selmon->sel || !mons->next) 1971 | return; 1972 | sendmon(selmon->sel, dirtomon(arg->i)); 1973 | } 1974 | 1975 | void 1976 | tile(Monitor *m) { 1977 | unsigned int i, n, h, mw, my, ty; 1978 | Client *c; 1979 | 1980 | for(n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); 1981 | if(n == 0) 1982 | return; 1983 | 1984 | if(n > m->nmasters[m->curtag]) 1985 | mw = m->nmasters[m->curtag] ? m->ww * m->mfacts[m->curtag] : 0; 1986 | else 1987 | mw = m->ww; 1988 | for(i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) 1989 | if(i < m->nmasters[m->curtag]) { 1990 | h = (m->wh - my) / (MIN(n, m->nmasters[m->curtag]) - i); 1991 | resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), False); 1992 | my += HEIGHT(c) + gap; 1993 | } 1994 | else { 1995 | h = (m->wh - ty) / (n - i); 1996 | resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), False); 1997 | ty += HEIGHT(c) + gap; 1998 | } 1999 | } 2000 | 2001 | void 2002 | togglebar(const Arg *arg) { 2003 | selmon->showbar = !selmon->showbar; 2004 | updatebarpos(selmon); 2005 | resizebarwin(selmon); 2006 | if(showsystray) { 2007 | XWindowChanges wc; 2008 | if(!selmon->showbar) 2009 | wc.y = -bh; 2010 | else if(selmon->showbar) { 2011 | wc.y = 0; 2012 | if(!selmon->topbar) 2013 | wc.y = selmon->mh - bh; 2014 | } 2015 | XConfigureWindow(dpy, systray->win, CWY, &wc); 2016 | } 2017 | arrange(selmon); 2018 | } 2019 | 2020 | void 2021 | togglefloating(const Arg *arg) { 2022 | if(!selmon->sel) 2023 | return; 2024 | if(selmon->sel->isfullscreen) /* no support for fullscreen windows */ 2025 | return; 2026 | selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; 2027 | if(selmon->sel->isfloating) 2028 | resize(selmon->sel, selmon->sel->x, selmon->sel->y, 2029 | selmon->sel->w, selmon->sel->h, False); 2030 | arrange(selmon); 2031 | } 2032 | 2033 | void 2034 | toggletag(const Arg *arg) { 2035 | unsigned int i, newtags; 2036 | 2037 | if(!selmon->sel) 2038 | return; 2039 | newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); 2040 | if(newtags) { 2041 | selmon->sel->tags = newtags; 2042 | if(newtags == ~0) { 2043 | selmon->prevtag = selmon->curtag; 2044 | selmon->curtag = 0; 2045 | } 2046 | if(!(newtags & 1 << (selmon->curtag - 1))) { 2047 | selmon->prevtag = selmon->curtag; 2048 | for (i=0; !(newtags & 1 << i); i++); 2049 | selmon->curtag = i + 1; 2050 | } 2051 | selmon->sel->tags = newtags; 2052 | selmon->lt[selmon->sellt] = selmon->lts[selmon->curtag]; 2053 | focus(NULL); 2054 | arrange(selmon); 2055 | } 2056 | } 2057 | 2058 | void 2059 | toggleview(const Arg *arg) { 2060 | unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); 2061 | 2062 | if(newtagset) { 2063 | selmon->tagset[selmon->seltags] = newtagset; 2064 | focus(NULL); 2065 | arrange(selmon); 2066 | } 2067 | } 2068 | 2069 | void 2070 | unfocus(Client *c, Bool setfocus) { 2071 | if(!c) 2072 | return; 2073 | grabbuttons(c, False); 2074 | XSetWindowBorder(dpy, c->win, scheme[0].border->pix); 2075 | if(setfocus) { 2076 | XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); 2077 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); 2078 | } 2079 | } 2080 | 2081 | void 2082 | unmanage(Client *c, Bool destroyed) { 2083 | Monitor *m = c->mon; 2084 | XWindowChanges wc; 2085 | 2086 | /* The server grab construct avoids race conditions. */ 2087 | detach(c); 2088 | detachstack(c); 2089 | if(!destroyed) { 2090 | wc.border_width = c->oldbw; 2091 | XGrabServer(dpy); 2092 | XSetErrorHandler(xerrordummy); 2093 | XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ 2094 | XUngrabButton(dpy, AnyButton, AnyModifier, c->win); 2095 | setclientstate(c, WithdrawnState); 2096 | XSync(dpy, False); 2097 | XSetErrorHandler(xerror); 2098 | XUngrabServer(dpy); 2099 | } 2100 | free(c); 2101 | focus(NULL); 2102 | updateclientlist(); 2103 | arrange(m); 2104 | } 2105 | 2106 | void 2107 | unmapnotify(XEvent *e) { 2108 | Client *c; 2109 | XUnmapEvent *ev = &e->xunmap; 2110 | 2111 | if((c = wintoclient(ev->window))) { 2112 | if(ev->send_event) 2113 | setclientstate(c, WithdrawnState); 2114 | else 2115 | unmanage(c, False); 2116 | } 2117 | else if((c = wintosystrayicon(ev->window))) { 2118 | removesystrayicon(c); 2119 | resizebarwin(selmon); 2120 | updatesystray(); 2121 | } 2122 | } 2123 | 2124 | void 2125 | updatebars(void) { 2126 | unsigned int w; 2127 | Monitor *m; 2128 | XSetWindowAttributes wa = { 2129 | .override_redirect = True, 2130 | .background_pixmap = ParentRelative, 2131 | .event_mask = ButtonPressMask|ExposureMask 2132 | }; 2133 | for(m = mons; m; m = m->next) { 2134 | if (m->barwin) 2135 | continue; 2136 | w = m->ww; 2137 | if(showsystray && m == selmon) 2138 | w -= getsystraywidth(); 2139 | m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen), 2140 | CopyFromParent, DefaultVisual(dpy, screen), 2141 | CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); 2142 | XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); 2143 | XMapRaised(dpy, m->barwin); 2144 | } 2145 | } 2146 | 2147 | void 2148 | updatebarpos(Monitor *m) { 2149 | m->wy = m->my; 2150 | m->wh = m->mh; 2151 | if(m->showbar) { 2152 | m->wh -= bh; 2153 | m->by = m->topbar ? m->wy : m->wy + m->wh; 2154 | m->wy = m->topbar ? m->wy + bh : m->wy; 2155 | } 2156 | else 2157 | m->by = -bh; 2158 | } 2159 | 2160 | void 2161 | updateclientlist() { 2162 | Client *c; 2163 | Monitor *m; 2164 | 2165 | XDeleteProperty(dpy, root, netatom[NetClientList]); 2166 | for(m = mons; m; m = m->next) 2167 | for(c = m->clients; c; c = c->next) 2168 | XChangeProperty(dpy, root, netatom[NetClientList], 2169 | XA_WINDOW, 32, PropModeAppend, 2170 | (unsigned char *) &(c->win), 1); 2171 | } 2172 | 2173 | Bool 2174 | updategeom(void) { 2175 | Bool dirty = False; 2176 | 2177 | #ifdef XINERAMA 2178 | if(XineramaIsActive(dpy)) { 2179 | int i, j, n, nn; 2180 | Client *c; 2181 | Monitor *m; 2182 | XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn); 2183 | XineramaScreenInfo *unique = NULL; 2184 | 2185 | for(n = 0, m = mons; m; m = m->next, n++); 2186 | /* only consider unique geometries as separate screens */ 2187 | if(!(unique = (XineramaScreenInfo *)malloc(sizeof(XineramaScreenInfo) * nn))) 2188 | die("fatal: could not malloc() %u bytes\n", sizeof(XineramaScreenInfo) * nn); 2189 | for(i = 0, j = 0; i < nn; i++) 2190 | if(isuniquegeom(unique, j, &info[i])) 2191 | memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); 2192 | XFree(info); 2193 | nn = j; 2194 | if(n <= nn) { 2195 | for(i = 0; i < (nn - n); i++) { /* new monitors available */ 2196 | for(m = mons; m && m->next; m = m->next); 2197 | if(m) 2198 | m->next = createmon(); 2199 | else 2200 | mons = createmon(); 2201 | } 2202 | for(i = 0, m = mons; i < nn && m; m = m->next, i++) 2203 | if(i >= n 2204 | || (unique[i].x_org != m->mx || unique[i].y_org != m->my 2205 | || unique[i].width != m->mw || unique[i].height != m->mh)) 2206 | { 2207 | dirty = True; 2208 | m->num = i; 2209 | m->mx = m->wx = unique[i].x_org; 2210 | m->my = m->wy = unique[i].y_org; 2211 | m->mw = m->ww = unique[i].width; 2212 | m->mh = m->wh = unique[i].height; 2213 | updatebarpos(m); 2214 | } 2215 | } 2216 | else { /* less monitors available nn < n */ 2217 | for(i = nn; i < n; i++) { 2218 | for(m = mons; m && m->next; m = m->next); 2219 | while(m->clients) { 2220 | dirty = True; 2221 | c = m->clients; 2222 | m->clients = c->next; 2223 | detachstack(c); 2224 | c->mon = mons; 2225 | attachaside(c); 2226 | attachstack(c); 2227 | } 2228 | if(m == selmon) 2229 | selmon = mons; 2230 | cleanupmon(m); 2231 | } 2232 | } 2233 | free(unique); 2234 | } 2235 | else 2236 | #endif /* XINERAMA */ 2237 | /* default monitor setup */ 2238 | { 2239 | if(!mons) 2240 | mons = createmon(); 2241 | if(mons->mw != sw || mons->mh != sh) { 2242 | dirty = True; 2243 | mons->mw = mons->ww = sw; 2244 | mons->mh = mons->wh = sh; 2245 | updatebarpos(mons); 2246 | } 2247 | } 2248 | if(dirty) { 2249 | selmon = mons; 2250 | selmon = wintomon(root); 2251 | } 2252 | return dirty; 2253 | } 2254 | 2255 | void 2256 | updatenumlockmask(void) { 2257 | unsigned int i, j; 2258 | XModifierKeymap *modmap; 2259 | 2260 | numlockmask = 0; 2261 | modmap = XGetModifierMapping(dpy); 2262 | for(i = 0; i < 8; i++) 2263 | for(j = 0; j < modmap->max_keypermod; j++) 2264 | if(modmap->modifiermap[i * modmap->max_keypermod + j] 2265 | == XKeysymToKeycode(dpy, XK_Num_Lock)) 2266 | numlockmask = (1 << i); 2267 | XFreeModifiermap(modmap); 2268 | } 2269 | 2270 | void 2271 | updatesizehints(Client *c) { 2272 | long msize; 2273 | XSizeHints size; 2274 | 2275 | if(!XGetWMNormalHints(dpy, c->win, &size, &msize)) 2276 | /* size is uninitialized, ensure that size.flags aren't used */ 2277 | size.flags = PSize; 2278 | if(size.flags & PBaseSize) { 2279 | c->basew = size.base_width; 2280 | c->baseh = size.base_height; 2281 | } 2282 | else if(size.flags & PMinSize) { 2283 | c->basew = size.min_width; 2284 | c->baseh = size.min_height; 2285 | } 2286 | else 2287 | c->basew = c->baseh = 0; 2288 | if(size.flags & PResizeInc) { 2289 | c->incw = size.width_inc; 2290 | c->inch = size.height_inc; 2291 | } 2292 | else 2293 | c->incw = c->inch = 0; 2294 | if(size.flags & PMaxSize) { 2295 | c->maxw = size.max_width; 2296 | c->maxh = size.max_height; 2297 | } 2298 | else 2299 | c->maxw = c->maxh = 0; 2300 | if(size.flags & PMinSize) { 2301 | c->minw = size.min_width; 2302 | c->minh = size.min_height; 2303 | } 2304 | else if(size.flags & PBaseSize) { 2305 | c->minw = size.base_width; 2306 | c->minh = size.base_height; 2307 | } 2308 | else 2309 | c->minw = c->minh = 0; 2310 | if(size.flags & PAspect) { 2311 | c->mina = (float)size.min_aspect.y / size.min_aspect.x; 2312 | c->maxa = (float)size.max_aspect.x / size.max_aspect.y; 2313 | } 2314 | else 2315 | c->maxa = c->mina = 0.0; 2316 | c->isfixed = (c->maxw && c->minw && c->maxh && c->minh 2317 | && c->maxw == c->minw && c->maxh == c->minh); 2318 | } 2319 | 2320 | void 2321 | updatetitle(Client *c) { 2322 | if(!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) 2323 | gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); 2324 | if(c->name[0] == '\0') /* hack to mark broken clients */ 2325 | strcpy(c->name, broken); 2326 | } 2327 | 2328 | void 2329 | updatestatus(void) { 2330 | if(!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) 2331 | strcpy(stext, "dwm-"VERSION); 2332 | drawbar(selmon); 2333 | } 2334 | 2335 | void 2336 | updatewindowtype(Client *c) { 2337 | Atom state = getatomprop(c, netatom[NetWMState]); 2338 | Atom wtype = getatomprop(c, netatom[NetWMWindowType]); 2339 | 2340 | if(state == netatom[NetWMFullscreen] || 2341 | (WIDTH(c) == (c->mon->mx + c->mon->mw) && (HEIGHT(c) == (c->mon->my + c->mon->mh)))) 2342 | setfullscreen(c, True); 2343 | if(wtype == netatom[NetWMWindowTypeDialog]) 2344 | c->isfloating = True; 2345 | } 2346 | 2347 | void 2348 | updatewmhints(Client *c) { 2349 | XWMHints *wmh; 2350 | 2351 | if((wmh = XGetWMHints(dpy, c->win))) { 2352 | if(c == selmon->sel && wmh->flags & XUrgencyHint) { 2353 | wmh->flags &= ~XUrgencyHint; 2354 | XSetWMHints(dpy, c->win, wmh); 2355 | } 2356 | else 2357 | c->isurgent = (wmh->flags & XUrgencyHint) ? True : False; 2358 | if(wmh->flags & InputHint) 2359 | c->neverfocus = !wmh->input; 2360 | else 2361 | c->neverfocus = False; 2362 | XFree(wmh); 2363 | } 2364 | } 2365 | 2366 | void 2367 | view(const Arg *arg) { 2368 | unsigned int i; 2369 | 2370 | if((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) 2371 | return; 2372 | selmon->seltags ^= 1; /* toggle sel tagset */ 2373 | if(arg->ui & TAGMASK) { 2374 | selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; 2375 | selmon->prevtag = selmon->curtag; 2376 | if(arg->ui == ~0) 2377 | selmon->curtag = 0; 2378 | else { 2379 | for (i=0; !(arg->ui & 1 << i); i++); 2380 | selmon->curtag = i + 1; 2381 | } 2382 | } else { 2383 | selmon->prevtag= selmon->curtag ^ selmon->prevtag; 2384 | selmon->curtag^= selmon->prevtag; 2385 | selmon->prevtag= selmon->curtag ^ selmon->prevtag; 2386 | } 2387 | selmon->lt[selmon->sellt]= selmon->lts[selmon->curtag]; 2388 | focus(NULL); 2389 | arrange(selmon); 2390 | } 2391 | 2392 | Client * 2393 | wintoclient(Window w) { 2394 | Client *c; 2395 | Monitor *m; 2396 | 2397 | for(m = mons; m; m = m->next) 2398 | for(c = m->clients; c; c = c->next) 2399 | if(c->win == w) 2400 | return c; 2401 | return NULL; 2402 | } 2403 | 2404 | Monitor * 2405 | wintomon(Window w) { 2406 | int x, y; 2407 | Client *c; 2408 | Monitor *m; 2409 | 2410 | if(w == root && getrootptr(&x, &y)) 2411 | return recttomon(x, y, 1, 1); 2412 | for(m = mons; m; m = m->next) 2413 | if(w == m->barwin) 2414 | return m; 2415 | if((c = wintoclient(w))) 2416 | return c->mon; 2417 | return selmon; 2418 | } 2419 | 2420 | /* There's no way to check accesses to destroyed windows, thus those cases are 2421 | * ignored (especially on UnmapNotify's). Other types of errors call Xlibs 2422 | * default error handler, which may call exit. */ 2423 | int 2424 | xerror(Display *dpy, XErrorEvent *ee) { 2425 | if(ee->error_code == BadWindow 2426 | || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) 2427 | || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) 2428 | || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) 2429 | || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) 2430 | || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) 2431 | || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) 2432 | || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) 2433 | || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) 2434 | return 0; 2435 | fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", 2436 | ee->request_code, ee->error_code); 2437 | return xerrorxlib(dpy, ee); /* may call exit */ 2438 | } 2439 | 2440 | int 2441 | xerrordummy(Display *dpy, XErrorEvent *ee) { 2442 | return 0; 2443 | } 2444 | 2445 | /* Startup Error handler to check if another window manager 2446 | * is already running. */ 2447 | int 2448 | xerrorstart(Display *dpy, XErrorEvent *ee) { 2449 | die("dwm: another window manager is already running\n"); 2450 | return -1; 2451 | } 2452 | 2453 | void 2454 | zoom(const Arg *arg) { 2455 | Client *c = selmon->sel; 2456 | 2457 | if(!selmon->lt[selmon->sellt]->arrange 2458 | || (selmon->sel && selmon->sel->isfloating)) 2459 | return; 2460 | if(c == nexttiled(selmon->clients)) 2461 | if(!c || !(c = nexttiled(c->next))) 2462 | return; 2463 | pop(c); 2464 | } 2465 | 2466 | void 2467 | bstack(Monitor *m) { 2468 | unsigned int i, n, w, mh, mx, tx; 2469 | Client *c; 2470 | 2471 | for(n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); 2472 | if(n == 0) 2473 | return; 2474 | 2475 | if(n > m->nmasters[m->curtag]) 2476 | mh = m->nmasters[m->curtag] ? m->wh * m->mfacts[m->curtag] : 0; 2477 | else 2478 | mh = m->wh; 2479 | for(i = mx = tx = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) 2480 | if(i < m->nmasters[m->curtag]) { 2481 | w = (m->ww - mx) / (MIN(n, m->nmasters[m->curtag]) - i); 2482 | resize(c, m->wx + mx, m->wy, w - (2*c->bw), mh - (2*c->bw), False); 2483 | mx += WIDTH(c) + gap; 2484 | } 2485 | else { 2486 | w = (m->ww - tx) / (n - i); 2487 | resize(c, m->wx + tx, m->wy + mh, w - (2*c->bw), m->wh - mh - (2*c->bw), False); 2488 | tx += WIDTH(c) + gap; 2489 | } 2490 | } 2491 | 2492 | unsigned int 2493 | getsystraywidth() { 2494 | unsigned int w = 0; 2495 | Client *i; 2496 | if(showsystray) 2497 | for(i = systray->icons; i; w += i->w + 1, i = i->next) ; 2498 | return w ? w + 1 : 1; 2499 | } 2500 | 2501 | void 2502 | removesystrayicon(Client *i) { 2503 | Client **ii; 2504 | 2505 | if(!showsystray || !i) 2506 | return; 2507 | for(ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next); 2508 | if(ii) 2509 | *ii = i->next; 2510 | free(i); 2511 | } 2512 | 2513 | void 2514 | resizebarwin(Monitor *m) { 2515 | unsigned int w = m->ww; 2516 | if(showsystray && m == selmon) 2517 | w -= getsystraywidth(); 2518 | XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh); 2519 | } 2520 | 2521 | void 2522 | resizerequest(XEvent *e) { 2523 | XResizeRequestEvent *ev = &e->xresizerequest; 2524 | Client *i; 2525 | 2526 | if((i = wintosystrayicon(ev->window))) { 2527 | updatesystrayicongeom(i, ev->width, ev->height); 2528 | resizebarwin(selmon); 2529 | updatesystray(); 2530 | } 2531 | } 2532 | 2533 | void 2534 | updatesystrayicongeom(Client *i, int w, int h) { 2535 | if(i) { 2536 | i->h = bh; 2537 | if(w == h) 2538 | i->w = bh; 2539 | else if(h == bh) 2540 | i->w = w; 2541 | else 2542 | i->w = (int) ((float)bh * ((float)w / (float)h)); 2543 | applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False); 2544 | /* force icons into the systray dimenons if they don't want to */ 2545 | if(i->h > bh) { 2546 | if(i->w == i->h) 2547 | i->w = bh; 2548 | else 2549 | i->w = (int) ((float)bh * ((float)i->w / (float)i->h)); 2550 | i->h = bh; 2551 | } 2552 | } 2553 | } 2554 | 2555 | void 2556 | updatesystrayiconstate(Client *i, XPropertyEvent *ev) { 2557 | long flags; 2558 | int code = 0; 2559 | 2560 | if(!showsystray || !i || ev->atom != xatom[XembedInfo] || 2561 | !(flags = getatomprop(i, xatom[XembedInfo]))) 2562 | return; 2563 | 2564 | if(flags & XEMBED_MAPPED && !i->tags) { 2565 | i->tags = 1; 2566 | code = XEMBED_WINDOW_ACTIVATE; 2567 | XMapRaised(dpy, i->win); 2568 | setclientstate(i, NormalState); 2569 | } 2570 | else if(!(flags & XEMBED_MAPPED) && i->tags) { 2571 | i->tags = 0; 2572 | code = XEMBED_WINDOW_DEACTIVATE; 2573 | XUnmapWindow(dpy, i->win); 2574 | setclientstate(i, WithdrawnState); 2575 | } 2576 | else 2577 | return; 2578 | sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0, 2579 | systray->win, XEMBED_EMBEDDED_VERSION); 2580 | } 2581 | 2582 | void 2583 | updatesystray(void) { 2584 | XSetWindowAttributes wa; 2585 | Client *i; 2586 | unsigned int x = selmon->mx + selmon->mw; 2587 | unsigned int w = 1; 2588 | ClrScheme *old = drw->scheme; 2589 | 2590 | if(!showsystray) 2591 | return; 2592 | if(!systray) { 2593 | /* init systray */ 2594 | drw_setscheme(drw, &scheme[9]); 2595 | if(!(systray = (Systray *)calloc(1, sizeof(Systray)))) 2596 | die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); 2597 | wa.event_mask = ButtonPressMask | ExposureMask; 2598 | wa.override_redirect = True; 2599 | wa.background_pixmap = ParentRelative; 2600 | #ifdef SOLARIZED_LIGHT 2601 | wa.background_pixel = drw->scheme->border->pix; 2602 | #endif 2603 | 2604 | #if defined(SOLARIZED_DARK) || defined(GRUVBOX) || defined(NORD) 2605 | wa.background_pixel = drw->scheme->bg->pix; 2606 | #endif 2607 | systray->win = XCreateSimpleWindow(dpy, root, x, selmon->by, w, bh, 0, 0, wa.background_pixel); 2608 | XSelectInput(dpy, systray->win, SubstructureNotifyMask); 2609 | XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32, 2610 | PropModeReplace, (unsigned char *)&systrayorientation, 1); 2611 | XChangeWindowAttributes(dpy, systray->win, CWEventMask | CWOverrideRedirect | CWBackPixel, &wa); 2612 | XMapRaised(dpy, systray->win); 2613 | XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime); 2614 | if(XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) { 2615 | sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0); 2616 | XSync(dpy, False); 2617 | } 2618 | else { 2619 | fprintf(stderr, "dwm: unable to obtain system tray.\n"); 2620 | free(systray); 2621 | systray = NULL; 2622 | return; 2623 | } 2624 | drw_setscheme(drw, old); 2625 | } 2626 | for(w = 0, i = systray->icons; i; i = i->next) { 2627 | XMapRaised(dpy, i->win); 2628 | w += 1; 2629 | XMoveResizeWindow(dpy, i->win, (i->x = w), 0, i->w, i->h); 2630 | w += i->w; 2631 | if(i->mon != selmon) 2632 | i->mon = selmon; 2633 | } 2634 | w = w ? w + 1 : 1; 2635 | x -= w; 2636 | XMoveResizeWindow(dpy, systray->win, x, selmon->by, w, bh); 2637 | XSync(dpy, False); 2638 | } 2639 | 2640 | Client * 2641 | wintosystrayicon(Window w) { 2642 | Client *i = NULL; 2643 | 2644 | if(!showsystray || !w) 2645 | return i; 2646 | for(i = systray->icons; i && i->win != w; i = i->next) ; 2647 | return i; 2648 | } 2649 | 2650 | int 2651 | main(int argc, char *argv[]) { 2652 | if(argc == 2 && !strcmp("-v", argv[1])) 2653 | die("dwm-"VERSION", © 2006-2014 dwm engineers, see LICENSE for details\n"); 2654 | else if(argc != 1) 2655 | die("usage: dwm [-v]\n"); 2656 | if(!setlocale(LC_CTYPE, "") || !XSupportsLocale()) 2657 | fputs("warning: no locale support\n", stderr); 2658 | if(!(dpy = XOpenDisplay(NULL))) 2659 | die("dwm: cannot open display\n"); 2660 | checkotherwm(); 2661 | setup(); 2662 | scan(); 2663 | run(); 2664 | cleanup(); 2665 | XCloseDisplay(dpy); 2666 | return EXIT_SUCCESS; 2667 | } 2668 | -------------------------------------------------------------------------------- /dwm.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Encoding=UTF-8 3 | Name=DWM 4 | Comment=Dynamic window manager 5 | Exec=/home/elken/.dwm/bin/start.sh 6 | Icon=dwm 7 | Type=XSession 8 | -------------------------------------------------------------------------------- /gaplessgrid.c: -------------------------------------------------------------------------------- 1 | void 2 | gaplessgrid(Monitor *m) { 3 | unsigned int n, cols, rows, cn, rn, i, cx, cy, cw, ch; 4 | Client *c; 5 | 6 | for(n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) ; 7 | if(n == 0) 8 | return; 9 | 10 | /* grid dimensions */ 11 | for(cols = 0; cols <= n/2; cols++) 12 | if(cols*cols >= n) 13 | break; 14 | if(n == 5) /* set layout against the general calculation: not 1:2:2, but 2:3 */ 15 | cols = 2; 16 | rows = n/cols; 17 | 18 | /* window geometries */ 19 | cw = cols ? m->ww / cols : m->ww; 20 | cn = 0; /* current column number */ 21 | rn = 0; /* current row number */ 22 | for(i = 0, c = nexttiled(m->clients); c; i++, c = nexttiled(c->next)) { 23 | if(i/rows + 1 > cols - n%cols) 24 | rows = n/cols + 1; 25 | ch = rows ? m->wh / rows : m->wh; 26 | cx = m->wx + cn*cw; 27 | cy = m->wy + rn*ch; 28 | resize(c, cx, cy, cw - 2 * c->bw, ch - 2 * c->bw, False); 29 | rn++; 30 | if(rn >= rows) { 31 | rn = 0; 32 | cn++; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /patches/01_XKeycodeFix.patch: -------------------------------------------------------------------------------- 1 | --- dwm.c.orig 2012-05-26 15:54:58.112895773 +0200 2 | +++ b/dwm-6.0/dwm.c 2012-05-26 15:54:39.636229535 +0200 3 | @@ -36,6 +36,7 @@ 4 | #include 5 | #include 6 | #include 7 | +#include 8 | #ifdef XINERAMA 9 | #include 10 | #endif /* XINERAMA */ 11 | @@ -1191,7 +1192,7 @@ 12 | XKeyEvent *ev; 13 | 14 | ev = &e->xkey; 15 | - keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); 16 | + keysym = XkbKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0, 0); 17 | for(i = 0; i < LENGTH(keys); i++) 18 | if(keysym == keys[i].keysym 19 | && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) 20 | -------------------------------------------------------------------------------- /patches/02_monoclecount.patch: -------------------------------------------------------------------------------- 1 | --- dwm.c.orig 2015-06-20 23:36:47.932029785 +0100 2 | +++ dwm.c 2015-06-20 23:46:33.400045948 +0100 3 | @@ -693,7 +693,8 @@ dirtomon(int dir) { 4 | void 5 | drawbar(Monitor *m) { 6 | int x, xx, w; 7 | - unsigned int i, occ = 0, urg = 0; 8 | + unsigned int i, occ = 0, urg = 0, a=0, s=0; 9 | + char posbuf[10]; 10 | Client *c; 11 | 12 | for(c = m->clients; c; c = c->next) { 13 | @@ -715,6 +716,17 @@ drawbar(Monitor *m) { 14 | drw_text(drw, x, 0, w, bh, m->ltsymbol, 0); 15 | x += w; 16 | xx = x; 17 | + if(m->lt[m->sellt]->arrange == monocle) { 18 | + for (c=nexttiled(m->clients), a=0, s=0; c; c=nexttiled(c->next), a++) 19 | + if(c== m->stack) 20 | + s=a; 21 | + if(!s && a) 22 | + s=a; 23 | + snprintf(posbuf, LENGTH(posbuf), "[%d/%d]", s, a); 24 | + w=TEXTW(posbuf); 25 | + drw_text(drw, x, 0, w, bh, posbuf, 0); 26 | + xx = x + w; 27 | + } 28 | if(m == selmon) { /* status is only drawn on selected monitor */ 29 | w = TEXTW(stext); 30 | x = m->ww - w; 31 | @@ -1095,8 +1107,6 @@ monocle(Monitor *m) { 32 | for(c = m->clients; c; c = c->next) 33 | if(ISVISIBLE(c)) 34 | n++; 35 | - if(n > 0) /* override layout symbol */ 36 | - snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); 37 | for(c = nexttiled(m->clients); c; c = nexttiled(c->next)) 38 | resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, False); 39 | } 40 | -------------------------------------------------------------------------------- /patches/03_notitle.patch: -------------------------------------------------------------------------------- 1 | --- dwm.c.orig 2015-06-20 23:52:20.532055531 +0100 2 | +++ dwm.c 2015-06-20 23:55:11.244060243 +0100 3 | @@ -65,8 +65,8 @@ enum { NetSupported, NetWMName, NetWMSta 4 | NetWMFullscreen, NetActiveWindow, NetWMWindowType, 5 | NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ 6 | enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ 7 | -enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, 8 | - ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ 9 | +enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkClientWin, 10 | + ClkRootWin, ClkLast }; /* clicks */ 11 | 12 | typedef union { 13 | int i; 14 | @@ -445,10 +445,8 @@ buttonpress(XEvent *e) { 15 | } 16 | else if(ev->x < x + blw) 17 | click = ClkLtSymbol; 18 | - else if(ev->x > selmon->ww - TEXTW(stext)) 19 | - click = ClkStatusText; 20 | else 21 | - click = ClkWinTitle; 22 | + click = ClkStatusText; 23 | } 24 | else if((c = wintoclient(ev->window))) { 25 | focus(c); 26 | @@ -752,15 +750,8 @@ drawbar(Monitor *m) { 27 | x = m->ww; 28 | if((w = x - xx) > bh) { 29 | x = xx; 30 | - if(m->sel) { 31 | - drw_setscheme(drw, m == selmon ? &scheme[SchemeSel] : &scheme[SchemeNorm]); 32 | - drw_text(drw, x, 0, w, bh, m->sel->name, 0); 33 | - drw_rect(drw, x, 0, w, bh, m->sel->isfixed, m->sel->isfloating, 0); 34 | - } 35 | - else { 36 | - drw_setscheme(drw, &scheme[SchemeNorm]); 37 | - drw_text(drw, x, 0, w, bh, NULL, 0); 38 | - } 39 | + drw_setscheme(drw, &scheme[SchemeNorm]); 40 | + drw_text(drw, x, 0, w, bh, NULL, 0); 41 | } 42 | drw_map(drw, m->barwin, 0, 0, m->ww, bh); 43 | } 44 | @@ -1241,11 +1232,8 @@ propertynotify(XEvent *e) { 45 | drawbars(); 46 | break; 47 | } 48 | - if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { 49 | + if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) 50 | updatetitle(c); 51 | - if(c == c->mon->sel) 52 | - drawbar(c->mon); 53 | - } 54 | if(ev->atom == netatom[NetWMWindowType]) 55 | updatewindowtype(c); 56 | } 57 | -------------------------------------------------------------------------------- /patches/04_attachaside.patch: -------------------------------------------------------------------------------- 1 | diff --git a/dwm.c b/dwm.c 2 | index 1bbb4b3..b2aa1c8 100644 3 | --- a/dwm.c 4 | +++ b/dwm.c 5 | @@ -146,6 +146,7 @@ static Bool applysizehints(Client *c, int *x, int *y, int *w, int *h, Bool inter 6 | static void arrange(Monitor *m); 7 | static void arrangemon(Monitor *m); 8 | static void attach(Client *c); 9 | +static void attachaside(Client *c); 10 | static void attachstack(Client *c); 11 | static void buttonpress(XEvent *e); 12 | static void checkotherwm(void); 13 | @@ -401,6 +402,17 @@ attach(Client *c) { 14 | } 15 | 16 | void 17 | +attachaside(Client *c) { 18 | + Client *at = nexttiled(c->mon->clients); 19 | + if(c->mon->sel == NULL || c->mon->sel->isfloating || !at) { 20 | + attach(c); 21 | + return; 22 | + } 23 | + c->next = at->next; 24 | + at->next = c; 25 | +} 26 | + 27 | +void 28 | attachstack(Client *c) { 29 | c->snext = c->mon->stack; 30 | c->mon->stack = c; 31 | @@ -1051,7 +1063,7 @@ manage(Window w, XWindowAttributes *wa) { 32 | c->isfloating = c->oldstate = trans != None || c->isfixed; 33 | if(c->isfloating) 34 | XRaiseWindow(dpy, c->win); 35 | - attach(c); 36 | + attachaside(c); 37 | attachstack(c); 38 | XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, 39 | (unsigned char *) &(c->win), 1); 40 | @@ -1383,7 +1395,7 @@ sendmon(Client *c, Monitor *m) { 41 | detachstack(c); 42 | c->mon = m; 43 | c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ 44 | - attach(c); 45 | + attachaside(c); 46 | attachstack(c); 47 | focus(NULL); 48 | arrange(NULL); 49 | @@ -1818,7 +1830,7 @@ updategeom(void) { 50 | m->clients = c->next; 51 | detachstack(c); 52 | c->mon = mons; 53 | - attach(c); 54 | + attachaside(c); 55 | attachstack(c); 56 | } 57 | if(m == selmon) 58 | -------------------------------------------------------------------------------- /patches/05_occupiedcol.patch: -------------------------------------------------------------------------------- 1 | --- dwm.c.orig 2015-06-21 17:00:38.286217538 +0100 2 | +++ dwm.c 2015-06-21 17:04:43.639317123 +0100 3 | @@ -715,10 +715,8 @@ drawbar(Monitor *m) { 4 | x = 0; 5 | for(i = 0; i < LENGTH(tags); i++) { 6 | w = TEXTW(tags[i]); 7 | - drw_setscheme(drw, m->tagset[m->seltags] & 1 << i ? &scheme[SchemeSel] : &scheme[SchemeNorm]); 8 | + drw_setscheme(drw, &scheme[m->tagset[m->seltags] & 1 << i) ? 10 : (urg & 1 << i ? 11 : (occ & 1 << i ? 12 : 9))]); 9 | drw_text(drw, x, 0, w, bh, tags[i], urg & 1 << i); 10 | - drw_rect(drw, x, 0, w, bh, m == selmon && selmon->sel && selmon->sel->tags & 1 << i, 11 | - occ & 1 << i, urg & 1 << i); 12 | x += w; 13 | } 14 | w = blw = TEXTW(m->ltsymbol); 15 | -------------------------------------------------------------------------------- /patches/06_uselessgaps.patch: -------------------------------------------------------------------------------- 1 | --- dwm.c.orig 2015-06-21 17:04:43.639317123 +0100 2 | +++ dwm.c 2015-06-21 17:05:39.833264741 +0100 3 | @@ -109,7 +109,8 @@ typedef struct { 4 | 5 | typedef struct { 6 | const char *symbol; 7 | - void (*arrange)(Monitor *); 8 | + Bool addgaps; 9 | + void (*arrange)(Monitor *); 10 | } Layout; 11 | 12 | struct Monitor { 13 | @@ -268,6 +269,7 @@ static Display *dpy; 14 | static Drw *drw; 15 | static Monitor *mons, *selmon; 16 | static Window root; 17 | +static int gap; 18 | 19 | /* configuration, allows nested code to access above variables */ 20 | #include "config.h" 21 | @@ -471,7 +473,7 @@ checkotherwm(void) { 22 | void 23 | cleanup(void) { 24 | Arg a = {.ui = ~0}; 25 | - Layout foo = { "", NULL }; 26 | + Layout foo = { "", False, NULL }; 27 | Monitor *m; 28 | 29 | view(&a); 30 | @@ -1265,10 +1267,11 @@ void 31 | resizeclient(Client *c, int x, int y, int w, int h) { 32 | XWindowChanges wc; 33 | 34 | - c->oldx = c->x; c->x = wc.x = x; 35 | - c->oldy = c->y; c->y = wc.y = y; 36 | - c->oldw = c->w; c->w = wc.width = w; 37 | - c->oldh = c->h; c->h = wc.height = h; 38 | + gap = c->isfloating ? 0 : c->mon->lt[c->mon->sellt]->addgaps ? gappx : 0; 39 | + c->oldx = c->x; c->x = wc.x = x + gap; 40 | + c->oldy = c->y; c->y = wc.y = y + gap; 41 | + c->oldw = c->w; c->w = wc.width = w - (gap ? (x + w + (c->bw * 2) == c->mon->mx + c->mon->mw ? 2 : 1) * gap : 0); 42 | + c->oldh = c->h; c->h = wc.height = h - (gap ? (y + h + (c->bw * 2) == c->mon->my + c->mon->mh ? 2 : 1) * gap : 0); 43 | wc.border_width = c->bw; 44 | XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); 45 | configure(c); 46 | @@ -1634,12 +1637,12 @@ tile(Monitor *m) { 47 | if(i < m->nmaster) { 48 | h = (m->wh - my) / (MIN(n, m->nmaster) - i); 49 | resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), False); 50 | - my += HEIGHT(c); 51 | + my += HEIGHT(c) + gap; 52 | } 53 | else { 54 | h = (m->wh - ty) / (n - i); 55 | resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), False); 56 | - ty += HEIGHT(c); 57 | + ty += HEIGHT(c) + gap; 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /patches/07_runorraise.patch: -------------------------------------------------------------------------------- 1 | --- dwm.c.orig 2015-06-21 17:08:15.778445179 +0100 2 | +++ dwm.c 2015-06-21 17:08:22.386328542 +0100 3 | @@ -197,6 +197,7 @@ static void resizeclient(Client *c, int 4 | static void resizemouse(const Arg *arg); 5 | static void restack(Monitor *m); 6 | static void run(void); 7 | +static void runorraise(const Arg *arg); 8 | static void scan(void); 9 | static Bool sendevent(Client *c, Atom proto); 10 | static void sendmon(Client *c, Monitor *m); 11 | @@ -1369,6 +1370,30 @@ run(void) { 12 | } 13 | 14 | void 15 | +runorraise(const Arg *arg) { 16 | + char *app = ((char **)arg->v)[4]; 17 | + Arg a = { .ui = ~0 }; 18 | + Monitor *mon; 19 | + Client *c; 20 | + XClassHint hint = { NULL, NULL }; 21 | + /* Tries to find the client */ 22 | + for (mon = mons; mon; mon = mon->next) { 23 | + for (c = mon->clients; c; c = c->next) { 24 | + XGetClassHint(dpy, c->win, &hint); 25 | + if (hint.res_class && strcmp(app, hint.res_class) == 0) { 26 | + a.ui = c->tags; 27 | + view(&a); 28 | + focus(c); 29 | + XRaiseWindow(dpy, c->win); 30 | + return; 31 | + } 32 | + } 33 | + } 34 | + /* Client not found: spawn it */ 35 | + spawn(arg); 36 | +} 37 | + 38 | +void 39 | scan(void) { 40 | unsigned int i, num; 41 | Window d1, d2, *wins = NULL; 42 | -------------------------------------------------------------------------------- /patches/08_betterborders.patch: -------------------------------------------------------------------------------- 1 | Author: Eric Pruitt, https://github.com/ericpruitt/ 2 | Description: This patch makes dwm remove borders when only one, non-floating 3 | window is visible. Additionally, any windows that are the same size as the 4 | monitor are considered full-screen and their borders removed accordingly. 5 | 6 | diff --git a/dwm.c b/dwm.c 7 | index ffc8864..3ce8ebe 100644 8 | --- a/dwm.c 9 | +++ b/dwm.c 10 | @@ -308,6 +308,34 @@ applyrules(Client *c) { 11 | c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; 12 | } 13 | 14 | +void 15 | +adjustborders(Monitor *m) { 16 | + Client *c, *l = NULL; 17 | + int visible = 0; 18 | + 19 | + for(c = m->clients; c; c = c->next) { 20 | + if (ISVISIBLE(c) && !c->isfloating && m->lt[m->sellt]->arrange) { 21 | + if (m->lt[m->sellt]->arrange == monocle) { 22 | + visible = 1; 23 | + c->oldbw = c->bw; 24 | + c->bw = 0; 25 | + } else { 26 | + visible++; 27 | + c->oldbw = c->bw; 28 | + c->bw = borderpx; 29 | + } 30 | + 31 | + l = c; 32 | + } 33 | + } 34 | + 35 | + if (l && visible == 1 && l->bw) { 36 | + l->oldbw = l->bw; 37 | + l->bw = 0; 38 | + resizeclient(l, l->x, l->y, l->w, l->h); 39 | + } 40 | +} 41 | + 42 | Bool 43 | applysizehints(Client *c, int *x, int *y, int *w, int *h, Bool interact) { 44 | Bool baseismin; 45 | @@ -376,10 +404,13 @@ applysizehints(Client *c, int *x, int *y, int *w, int *h, Bool interact) { 46 | 47 | void 48 | arrange(Monitor *m) { 49 | - if(m) 50 | + if(m) { 51 | + adjustborders(m); 52 | showhide(m->stack); 53 | - else for(m = mons; m; m = m->next) 54 | + } else for(m = mons; m; m = m->next) { 55 | + adjustborders(m); 56 | showhide(m->stack); 57 | + } 58 | if(m) { 59 | arrangemon(m); 60 | restack(m); 61 | @@ -1036,7 +1067,20 @@ manage(Window w, XWindowAttributes *wa) { 62 | /* only fix client y-offset, if the client center might cover the bar */ 63 | c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) 64 | && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); 65 | - c->bw = borderpx; 66 | + 67 | + updatewindowtype(c); 68 | + if (c->isfloating) { 69 | + c->bw = c->isfullscreen ? 0 : borderpx; 70 | + } else { 71 | + c->bw = 0; 72 | + for(t = c->mon->clients; t; t = c->next) { 73 | + if (!t->isfloating && c != t && c->tags & t->tags) { 74 | + c->bw = borderpx; 75 | + break; 76 | + } 77 | + } 78 | + adjustborders(c->mon); 79 | + } 80 | 81 | wc.border_width = c->bw; 82 | XConfigureWindow(dpy, w, CWBorderWidth, &wc); 83 | @@ -1933,7 +1977,8 @@ updatewindowtype(Client *c) { 84 | Atom state = getatomprop(c, netatom[NetWMState]); 85 | Atom wtype = getatomprop(c, netatom[NetWMWindowType]); 86 | 87 | - if(state == netatom[NetWMFullscreen]) 88 | + if(state == netatom[NetWMFullscreen] || 89 | + (WIDTH(c) == (c->mon->mx + c->mon->mw) && (HEIGHT(c) == (c->mon->my + c->mon->mh)))) 90 | setfullscreen(c, True); 91 | if(wtype == netatom[NetWMWindowTypeDialog]) 92 | c->isfloating = True; 93 | -------------------------------------------------------------------------------- /patches/09_selfrestart.patch: -------------------------------------------------------------------------------- 1 | --- dwm.c.orig 2015-06-21 17:11:43.191871154 +0100 2 | +++ dwm.c 2015-06-21 17:13:45.572830971 +0100 3 | @@ -200,6 +200,7 @@ static void run(void); 4 | static void runorraise(const Arg *arg); 5 | static void scan(void); 6 | static Bool sendevent(Client *c, Atom proto); 7 | +static void self_restart(const Arg *arg); 8 | static void sendmon(Client *c, Monitor *m); 9 | static void setclientstate(Client *c, long state); 10 | static void setfocus(Client *c); 11 | @@ -1464,6 +1465,16 @@ scan(void) { 12 | } 13 | 14 | void 15 | +self_restart(const Arg *arg) { 16 | + char* const argv[] = {(char*)dwmpath, NULL}; 17 | + 18 | + if(argv[0] == NULL) { 19 | + return; 20 | + } 21 | + 22 | + execv(argv[0], argv); 23 | +} 24 | +void 25 | sendmon(Client *c, Monitor *m) { 26 | if(c->mon == m) 27 | return; 28 | -------------------------------------------------------------------------------- /patches/10_stacker.patch: -------------------------------------------------------------------------------- 1 | --- dwm.c.orig 2015-06-21 17:13:45.572830971 +0100 2 | +++ dwm.c 2015-06-21 17:15:36.124020703 +0100 3 | @@ -48,15 +48,21 @@ 4 | /* macros */ 5 | #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) 6 | #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) 7 | +#define GETINC(X) ((X) - 2000) 8 | +#define INC(X) ((X) + 2000) 9 | #define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ 10 | * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) 11 | +#define ISINC(X) ((X) > 1000 && (X) < 3000) 12 | #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) 13 | +#define PREVSEL 3000 14 | #define LENGTH(X) (sizeof X / sizeof X[0]) 15 | +#define MOD(N,M) ((N)%(M) < 0 ? (N)%(M) + (M) : (N)%(M)) 16 | #define MOUSEMASK (BUTTONMASK|PointerMotionMask) 17 | #define WIDTH(X) ((X)->w + 2 * (X)->bw) 18 | #define HEIGHT(X) ((X)->h + 2 * (X)->bw) 19 | #define TAGMASK ((1 << LENGTH(tags)) - 1) 20 | #define TEXTW(X) (drw_text(drw, 0, 0, 0, 0, (X), 0) + drw->fonts[0]->h) 21 | +#define TRUNC(X,A,B) (MAX((A), MIN((X), (B)))) 22 | 23 | /* enums */ 24 | enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ 25 | @@ -190,6 +196,7 @@ static void movemouse(const Arg *arg); 26 | static Client *nexttiled(Client *c); 27 | static void pop(Client *); 28 | static void propertynotify(XEvent *e); 29 | +static void pushstack(const Arg *arg); 30 | static void quit(const Arg *arg); 31 | static Monitor *recttomon(int x, int y, int w, int h); 32 | static void resize(Client *c, int x, int y, int w, int h, Bool interact); 33 | @@ -211,6 +218,7 @@ static void setup(void); 34 | static void showhide(Client *c); 35 | static void sigchld(int unused); 36 | static void spawn(const Arg *arg); 37 | +static int stackpos(const Arg *arg); 38 | static void tag(const Arg *arg); 39 | static void tagmon(const Arg *arg); 40 | static void tile(Monitor *); 41 | @@ -875,28 +883,16 @@ focusmon(const Arg *arg) { 42 | 43 | void 44 | focusstack(const Arg *arg) { 45 | - Client *c = NULL, *i; 46 | + int i = stackpos(arg); 47 | + Client *c, *p; 48 | 49 | - if(!selmon->sel) 50 | + if(i < 0) 51 | return; 52 | - if(arg->i > 0) { 53 | - for(c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); 54 | - if(!c) 55 | - for(c = selmon->clients; c && !ISVISIBLE(c); c = c->next); 56 | - } 57 | - else { 58 | - for(i = selmon->clients; i != selmon->sel; i = i->next) 59 | - if(ISVISIBLE(i)) 60 | - c = i; 61 | - if(!c) 62 | - for(; i; i = i->next) 63 | - if(ISVISIBLE(i)) 64 | - c = i; 65 | - } 66 | - if(c) { 67 | - focus(c); 68 | - restack(selmon); 69 | - } 70 | + 71 | + for(p = NULL, c = selmon->clients; c && (i || !ISVISIBLE(c)); 72 | + i -= ISVISIBLE(c) ? 1 : 0, p = c, c = c->next); 73 | + focus(c ? c : p); 74 | + restack(selmon); 75 | } 76 | 77 | Atom 78 | @@ -1286,6 +1282,29 @@ propertynotify(XEvent *e) { 79 | } 80 | 81 | void 82 | +pushstack(const Arg *arg) { 83 | + int i = stackpos(arg); 84 | + Client *sel = selmon->sel, *c, *p; 85 | + 86 | + if(i < 0) 87 | + return; 88 | + else if(i == 0) { 89 | + detach(sel); 90 | + attach(sel); 91 | + } 92 | + else { 93 | + for(p = NULL, c = selmon->clients; c; p = c, c = c->next) 94 | + if(!(i -= (ISVISIBLE(c) && c != sel))) 95 | + break; 96 | + c = c ? c : p; 97 | + detach(sel); 98 | + sel->next = c->next; 99 | + c->next = sel; 100 | + } 101 | + arrange(selmon); 102 | +} 103 | + 104 | +void 105 | quit(const Arg *arg) { 106 | running = False; 107 | } 108 | @@ -1684,6 +1703,36 @@ spawn(const Arg *arg) { 109 | } 110 | } 111 | 112 | +int 113 | +stackpos(const Arg *arg) { 114 | + int n, i; 115 | + Client *c, *l; 116 | + 117 | + if(!selmon->clients) 118 | + return -1; 119 | + 120 | + if(arg->i == PREVSEL) { 121 | + for(l = selmon->stack; l && (!ISVISIBLE(l) || l == selmon->sel); l = l->snext); 122 | + if(!l) 123 | + return -1; 124 | + for(i = 0, c = selmon->clients; c != l; i += ISVISIBLE(c) ? 1 : 0, c = c->next); 125 | + return i; 126 | + } 127 | + else if(ISINC(arg->i)) { 128 | + if(!selmon->sel) 129 | + return -1; 130 | + for(i = 0, c = selmon->clients; c != selmon->sel; i += ISVISIBLE(c) ? 1 : 0, c = c->next); 131 | + for(n = i; c; n += ISVISIBLE(c) ? 1 : 0, c = c->next); 132 | + return MOD(i + GETINC(arg->i), n); 133 | + } 134 | + else if(arg->i < 0) { 135 | + for(i = 0, c = selmon->clients; c; i += ISVISIBLE(c) ? 1 : 0, c = c->next); 136 | + return MAX(i + arg->i, 0); 137 | + } 138 | + else 139 | + return arg->i; 140 | +} 141 | + 142 | void 143 | tag(const Arg *arg) { 144 | if(selmon->sel && arg->ui & TAGMASK) { 145 | -------------------------------------------------------------------------------- /patches/11_pertag2.patch: -------------------------------------------------------------------------------- 1 | --- dwm.c.orig 2015-06-21 17:15:36.124020703 +0100 2 | +++ dwm.c 2015-06-21 17:16:46.538880924 +0100 3 | @@ -121,8 +121,6 @@ typedef struct { 4 | 5 | struct Monitor { 6 | char ltsymbol[16]; 7 | - float mfact; 8 | - int nmaster; 9 | int num; 10 | int by; /* bar geometry */ 11 | int mx, my, mw, mh; /* screen size */ 12 | @@ -138,9 +136,21 @@ struct Monitor { 13 | Monitor *next; 14 | Window barwin; 15 | const Layout *lt[2]; 16 | + int curtag; 17 | + int prevtag; 18 | + const Layout **lts; 19 | + double *mfacts; 20 | + int *nmasters; 21 | }; 22 | 23 | typedef struct { 24 | + const char *name; 25 | + const Layout *layout; 26 | + float mfact; 27 | + int nmaster; 28 | +} Tag; 29 | + 30 | +typedef struct { 31 | const char *class; 32 | const char *instance; 33 | const char *title; 34 | @@ -246,6 +256,7 @@ static int xerror(Display *dpy, XErrorEv 35 | static int xerrordummy(Display *dpy, XErrorEvent *ee); 36 | static int xerrorstart(Display *dpy, XErrorEvent *ee); 37 | static void zoom(const Arg *arg); 38 | +static void bstack(Monitor *m); 39 | 40 | /* variables */ 41 | static const char broken[] = "broken"; 42 | @@ -480,7 +491,7 @@ buttonpress(XEvent *e) { 43 | if(ev->window == selmon->barwin) { 44 | i = x = 0; 45 | do 46 | - x += TEXTW(tags[i]); 47 | + x += TEXTW(tags[i].name); 48 | while(ev->x >= x && ++i < LENGTH(tags)); 49 | if(i < LENGTH(tags)) { 50 | click = ClkTagBar; 51 | @@ -552,6 +563,9 @@ cleanupmon(Monitor *mon) { 52 | } 53 | XUnmapWindow(dpy, mon->barwin); 54 | XDestroyWindow(dpy, mon->barwin); 55 | + free(mon->mfacts); 56 | + free(mon->nmasters); 57 | + free(mon->lts); 58 | free(mon); 59 | } 60 | 61 | @@ -684,17 +698,31 @@ configurerequest(XEvent *e) { 62 | Monitor * 63 | createmon(void) { 64 | Monitor *m; 65 | + int i, numtags = LENGTH(tags) + 1; 66 | 67 | if(!(m = (Monitor *)calloc(1, sizeof(Monitor)))) 68 | die("fatal: could not malloc() %u bytes\n", sizeof(Monitor)); 69 | + if(!(m->mfacts = calloc(numtags, sizeof(double)))) 70 | + die("fatal: could not malloc() %u bytes\n", sizeof(double) * numtags); 71 | + if(!(m->nmasters = calloc(numtags, sizeof(int)))) 72 | + die("fatal: could not malloc() %u bytes\n", sizeof(int) * numtags); 73 | + if(!(m->lts = calloc(numtags, sizeof(Layout *)))) 74 | + die("fatal: could not malloc() %u bytes\n", sizeof(Layout *) * numtags); 75 | m->tagset[0] = m->tagset[1] = 1; 76 | - m->mfact = mfact; 77 | - m->nmaster = nmaster; 78 | + m->mfacts[0] = mfact; 79 | + m->nmasters[0] = nmaster; 80 | + m->lts[0] = &layouts[0]; 81 | m->showbar = showbar; 82 | m->topbar = topbar; 83 | - m->lt[0] = &layouts[0]; 84 | + m->curtag = m->prevtag = 1; 85 | + for(i = 1; i < numtags; i++) { 86 | + m->mfacts[i] = tags[i - 1].mfact < 0 ? mfact : tags[i - 1].mfact; 87 | + m->nmasters[i] = tags[i - 1].nmaster < 0 ? nmaster : tags[i - 1].nmaster; 88 | + m->lts[i] = tags[i - 1].layout; 89 | + } 90 | + m->lt[0] = m->lts[m->curtag]; 91 | m->lt[1] = &layouts[1 % LENGTH(layouts)]; 92 | - strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); 93 | + strncpy(m->ltsymbol, m->lt[0]->symbol, sizeof m->ltsymbol); 94 | return m; 95 | } 96 | 97 | @@ -757,9 +785,9 @@ drawbar(Monitor *m) { 98 | } 99 | x = 0; 100 | for(i = 0; i < LENGTH(tags); i++) { 101 | - w = TEXTW(tags[i]); 102 | + w = TEXTW(tags[i].name); 103 | drw_setscheme(drw, &scheme[m->tagset[m->seltags] & 1 << i) ? 10 : (urg & 1 << i ? 11 : (occ & 1 << i ? 12 : 9))]); 104 | - drw_text(drw, x, 0, w, bh, tags[i], urg & 1 << i); 105 | + drw_text(drw, x, 0, w, bh, tags[i].name, urg & 1 << i); 106 | x += w; 107 | } 108 | w = blw = TEXTW(m->ltsymbol); 109 | @@ -1002,7 +1030,7 @@ grabkeys(void) { 110 | 111 | void 112 | incnmaster(const Arg *arg) { 113 | - selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); 114 | + selmon->nmasters[selmon->curtag] = MAX(selmon->nmasters[selmon->curtag] + arg->i, 0); 115 | arrange(selmon); 116 | } 117 | 118 | @@ -1584,7 +1612,7 @@ setlayout(const Arg *arg) { 119 | if(!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) 120 | selmon->sellt ^= 1; 121 | if(arg && arg->v) 122 | - selmon->lt[selmon->sellt] = (Layout *)arg->v; 123 | + selmon->lt[selmon->sellt] = selmon->lts[selmon->curtag] = (Layout *)arg->v; 124 | strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); 125 | if(selmon->sel) 126 | arrange(selmon); 127 | @@ -1599,10 +1627,10 @@ setmfact(const Arg *arg) { 128 | 129 | if(!arg || !selmon->lt[selmon->sellt]->arrange) 130 | return; 131 | - f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; 132 | + f = arg->f < 1.0 ? arg->f + selmon->mfacts[selmon->curtag] : arg->f - 1.0; 133 | if(f < 0.1 || f > 0.9) 134 | return; 135 | - selmon->mfact = f; 136 | + selmon->mfacts[selmon->curtag] = f; 137 | arrange(selmon); 138 | } 139 | 140 | @@ -1758,13 +1786,13 @@ tile(Monitor *m) { 141 | if(n == 0) 142 | return; 143 | 144 | - if(n > m->nmaster) 145 | - mw = m->nmaster ? m->ww * m->mfact : 0; 146 | + if(n > m->nmasters[m->curtag]) 147 | + mw = m->nmasters[m->curtag] ? m->ww * m->mfacts[m->curtag] : 0; 148 | else 149 | mw = m->ww; 150 | for(i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) 151 | - if(i < m->nmaster) { 152 | - h = (m->wh - my) / (MIN(n, m->nmaster) - i); 153 | + if(i < m->nmasters[m->curtag]) { 154 | + h = (m->wh - my) / (MIN(n, m->nmasters[m->curtag]) - i); 155 | resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), False); 156 | my += HEIGHT(c) + gap; 157 | } 158 | @@ -1798,13 +1826,24 @@ togglefloating(const Arg *arg) { 159 | 160 | void 161 | toggletag(const Arg *arg) { 162 | - unsigned int newtags; 163 | + unsigned int i, newtags; 164 | 165 | if(!selmon->sel) 166 | return; 167 | newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); 168 | if(newtags) { 169 | selmon->sel->tags = newtags; 170 | + if(newtags == ~0) { 171 | + selmon->prevtag = selmon->curtag; 172 | + selmon->curtag = 0; 173 | + } 174 | + if(!(newtags & 1 << (selmon->curtag - 1))) { 175 | + selmon->prevtag = selmon->curtag; 176 | + for (i=0; !(newtags & 1 << i); i++); 177 | + selmon->curtag = i + 1; 178 | + } 179 | + selmon->sel->tags = newtags; 180 | + selmon->lt[selmon->sellt] = selmon->lts[selmon->curtag]; 181 | focus(NULL); 182 | arrange(selmon); 183 | } 184 | @@ -2111,11 +2150,26 @@ updatewmhints(Client *c) { 185 | 186 | void 187 | view(const Arg *arg) { 188 | + unsigned int i; 189 | + 190 | if((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) 191 | return; 192 | selmon->seltags ^= 1; /* toggle sel tagset */ 193 | - if(arg->ui & TAGMASK) 194 | + if(arg->ui & TAGMASK) { 195 | selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; 196 | + selmon->prevtag = selmon->curtag; 197 | + if(arg->ui == ~0) 198 | + selmon->curtag = 0; 199 | + else { 200 | + for (i=0; !(arg->ui & 1 << i); i++); 201 | + selmon->curtag = i + 1; 202 | + } 203 | + } else { 204 | + selmon->prevtag= selmon->curtag ^ selmon->prevtag; 205 | + selmon->curtag^= selmon->prevtag; 206 | + selmon->prevtag= selmon->curtag ^ selmon->prevtag; 207 | + } 208 | + selmon->lt[selmon->sellt]= selmon->lts[selmon->curtag]; 209 | focus(NULL); 210 | arrange(selmon); 211 | } 212 | @@ -2194,6 +2248,32 @@ zoom(const Arg *arg) { 213 | pop(c); 214 | } 215 | 216 | +void 217 | +bstack(Monitor *m) { 218 | + unsigned int i, n, w, mh, mx, tx; 219 | + Client *c; 220 | + 221 | + for(n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); 222 | + if(n == 0) 223 | + return; 224 | + 225 | + if(n > m->nmasters[m->curtag]) 226 | + mh = m->nmasters[m->curtag] ? m->wh * m->mfacts[m->curtag] : 0; 227 | + else 228 | + mh = m->wh; 229 | + for(i = mx = tx = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) 230 | + if(i < m->nmasters[m->curtag]) { 231 | + w = (m->ww - mx) / (MIN(n, m->nmasters[m->curtag]) - i); 232 | + resize(c, m->wx + mx, m->wy, w - (2*c->bw), mh - (2*c->bw), False); 233 | + mx += WIDTH(c) + gap; 234 | + } 235 | + else { 236 | + w = (m->ww - tx) / (n - i); 237 | + resize(c, m->wx + tx, m->wy + mh, w - (2*c->bw), m->wh - mh - (2*c->bw), False); 238 | + tx += WIDTH(c) + gap; 239 | + } 240 | +} 241 | + 242 | int 243 | main(int argc, char *argv[]) { 244 | if(argc == 2 && !strcmp("-v", argv[1])) 245 | -------------------------------------------------------------------------------- /patches/12_statuscolors.patch: -------------------------------------------------------------------------------- 1 | --- dwm.c.orig 2015-06-21 17:38:47.616569333 +0100 2 | +++ dwm.c 2015-06-21 17:39:31.103884473 +0100 3 | @@ -56,6 +56,7 @@ 4 | #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) 5 | #define PREVSEL 3000 6 | #define LENGTH(X) (sizeof X / sizeof X[0]) 7 | +#define MAXCOLORS 13 8 | #define MOD(N,M) ((N)%(M) < 0 ? (N)%(M) + (M) : (N)%(M)) 9 | #define MOUSEMASK (BUTTONMASK|PointerMotionMask) 10 | #define WIDTH(X) ((X)->w + 2 * (X)->bw) 11 | @@ -285,7 +286,7 @@ static void (*handler[LASTEvent]) (XEven 12 | static Atom wmatom[WMLast], netatom[NetLast]; 13 | static Bool running = True; 14 | static Cur *cursor[CurLast]; 15 | -static ClrScheme scheme[SchemeLast]; 16 | +static ClrScheme scheme[MAXCOLORS]; 17 | static Display *dpy; 18 | static Drw *drw; 19 | static Monitor *mons, *selmon; 20 | @@ -786,12 +787,12 @@ drawbar(Monitor *m) { 21 | x = 0; 22 | for(i = 0; i < LENGTH(tags); i++) { 23 | w = TEXTW(tags[i].name); 24 | - drw_setscheme(drw, &scheme[m->tagset[m->seltags] & 1 << i) ? 10 : (urg & 1 << i ? 11 : (occ & 1 << i ? 12 : 9))]); 25 | + drw_setscheme(drw, &scheme[(m->tagset[m->seltags] & 1 << i) ? 10 : (urg & 1 << i ? 11 : (occ & 1 << i ? 12 : 9))]); 26 | drw_text(drw, x, 0, w, bh, tags[i].name, urg & 1 << i); 27 | x += w; 28 | } 29 | w = blw = TEXTW(m->ltsymbol); 30 | - drw_setscheme(drw, &scheme[SchemeNorm]); 31 | + drw_setscheme(drw, &scheme[9]); 32 | drw_text(drw, x, 0, w, bh, m->ltsymbol, 0); 33 | x += w; 34 | xx = x; 35 | @@ -819,7 +820,7 @@ drawbar(Monitor *m) { 36 | x = m->ww; 37 | if((w = x - xx) > bh) { 38 | x = xx; 39 | - drw_setscheme(drw, &scheme[SchemeNorm]); 40 | + drw_setscheme(drw, &scheme[9]); 41 | drw_text(drw, x, 0, w, bh, NULL, 0); 42 | } 43 | drw_map(drw, m->barwin, 0, 0, m->ww, bh); 44 | @@ -876,7 +877,7 @@ focus(Client *c) { 45 | detachstack(c); 46 | attachstack(c); 47 | grabbuttons(c, True); 48 | - XSetWindowBorder(dpy, c->win, scheme[SchemeSel].border->pix); 49 | + XSetWindowBorder(dpy, c->win, scheme[1].border->pix); 50 | setfocus(c); 51 | } 52 | else { 53 | @@ -1125,7 +1126,7 @@ manage(Window w, XWindowAttributes *wa) 54 | 55 | wc.border_width = c->bw; 56 | XConfigureWindow(dpy, w, CWBorderWidth, &wc); 57 | - XSetWindowBorder(dpy, w, scheme[SchemeNorm].border->pix); 58 | + XSetWindowBorder(dpy, w, scheme[0].border->pix); 59 | configure(c); /* propagates border_width, if size doesn't change */ 60 | updatewindowtype(c); 61 | updatesizehints(c); 62 | @@ -1670,12 +1671,12 @@ setup(void) { 63 | cursor[CurResize] = drw_cur_create(drw, XC_sizing); 64 | cursor[CurMove] = drw_cur_create(drw, XC_fleur); 65 | /* init appearance */ 66 | - scheme[SchemeNorm].border = drw_clr_create(drw, normbordercolor); 67 | - scheme[SchemeNorm].bg = drw_clr_create(drw, normbgcolor); 68 | - scheme[SchemeNorm].fg = drw_clr_create(drw, normfgcolor); 69 | - scheme[SchemeSel].border = drw_clr_create(drw, selbordercolor); 70 | - scheme[SchemeSel].bg = drw_clr_create(drw, selbgcolor); 71 | - scheme[SchemeSel].fg = drw_clr_create(drw, selfgcolor); 72 | + for(int i = 0; i < NUMCOLORS; i++){ 73 | + scheme[i].border = drw_clr_create(drw, colors[i][0]); 74 | + scheme[i].fg = drw_clr_create(drw, colors[i][1]); 75 | + scheme[i].bg = drw_clr_create(drw, colors[i][2]); 76 | + } 77 | + 78 | /* init bars */ 79 | updatebars(); 80 | updatestatus(); 81 | @@ -1865,7 +1866,7 @@ unfocus(Client *c, Bool setfocus) { 82 | if(!c) 83 | return; 84 | grabbuttons(c, False); 85 | - XSetWindowBorder(dpy, c->win, scheme[SchemeNorm].border->pix); 86 | + XSetWindowBorder(dpy, c->win, scheme[0].border->pix); 87 | if(setfocus) { 88 | XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); 89 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); 90 | --- dwm.c 2015-06-21 17:46:34.971243412 +0100 91 | +++ dwm.c.orig 2015-06-21 17:47:17.849574087 +0100 92 | @@ -92,6 +92,7 @@ enum { Manager, Xembed, XembedInfo, XLas 93 | enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ 94 | enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkClientWin, 95 | ClkRootWin, ClkLast }; /* clicks */ 96 | +enum { ColBorder, ColFG, ColBG, ColLast }; 97 | 98 | typedef union { 99 | int i; 100 | --- dwm.c.orig 2015-06-21 17:48:33.562392986 +0100 101 | +++ dwm.c 2015-06-21 17:48:39.905294079 +0100 102 | @@ -1851,8 +1851,6 @@ sigchld(int unused) { 103 | 104 | void 105 | spawn(const Arg *arg) { 106 | - if(arg->v == dmenucmd) 107 | - dmenumon[0] = '0' + selmon->num; 108 | if(fork() == 0) { 109 | if(dpy) 110 | close(ConnectionNumber(dpy)); 111 | --- drw.h.orig 2015-06-21 16:10:39.056738091 +0100 112 | +++ drw.h 2015-06-21 17:53:29.621781779 +0100 113 | @@ -67,8 +67,9 @@ void drw_setfont(Drw *drw, Fnt *font); 114 | void drw_setscheme(Drw *drw, ClrScheme *scheme); 115 | 116 | /* Drawing functions */ 117 | -void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int empty, int invert); 118 | -int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int invert); 119 | +int drw_colored_text(Drw *drw, ClrScheme *scheme, int numcolors, int x, int y, unsigned int w, unsigned int h, char *text); 120 | +void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int empty); 121 | +int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int pad); 122 | 123 | /* Map functions */ 124 | void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); 125 | --- drw.c.orig 2015-06-21 17:57:05.438355613 +0100 126 | +++ drw.c 2015-06-21 17:58:33.103504192 +0100 127 | @@ -202,13 +202,38 @@ drw_setscheme(Drw *drw, ClrScheme *schem 128 | drw->scheme = scheme; 129 | } 130 | 131 | +int 132 | +drw_colored_text(Drw *drw, ClrScheme *scheme, int numcolors, int x, int y, unsigned int w, unsigned int h, char *text) { 133 | + if(!drw || !drw->fontcount || !drw->scheme) 134 | + return 0; 135 | + 136 | + char *buf = text, *ptr = buf, c =1; 137 | + int i; 138 | + 139 | + while(*ptr) { 140 | + for(i = 0; *ptr < 0 || *ptr > numcolors; i++, ptr++); 141 | + if(!*ptr) 142 | + break; 143 | + c = *ptr; 144 | + *ptr = 0; 145 | + if(i) { 146 | + x = drw_text(drw, x, y, w, h, buf, 0); 147 | + } 148 | + *ptr = c; 149 | + drw_setscheme(drw, &scheme[c-1]); 150 | + buf = ++ptr; 151 | + } 152 | + drw_text(drw, x, y, w, h, buf, 0); 153 | + return x; 154 | +} 155 | + 156 | void 157 | -drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int empty, int invert) { 158 | +drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int empty) { 159 | int dx; 160 | 161 | if(!drw || !drw->fontcount || !drw->scheme) 162 | return; 163 | - XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme->bg->pix : drw->scheme->fg->pix); 164 | + XSetForeground(drw->dpy, drw->gc, drw->scheme->fg->pix); 165 | dx = (drw->fonts[0]->ascent + drw->fonts[0]->descent + 2) / 4; 166 | if(filled) 167 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x+1, y+1, dx+1, dx+1); 168 | @@ -219,7 +244,7 @@ drw_rect(Drw *drw, int x, int y, unsigne 169 | int 170 | drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int invert) { 171 | char buf[1024]; 172 | - int tx, ty, th; 173 | + int tx, ty; 174 | Extnts tex; 175 | Colormap cmap; 176 | Visual *vis; 177 | @@ -242,7 +267,7 @@ drw_text(Drw *drw, int x, int y, unsigne 178 | if (!drw || !drw->scheme) { 179 | return 0; 180 | } else if (render) { 181 | - XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme->fg->pix : drw->scheme->bg->pix); 182 | + XSetForeground(drw->dpy, drw->gc, drw->scheme->bg->pix); 183 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); 184 | } 185 | 186 | @@ -294,10 +319,10 @@ drw_text(Drw *drw, int x, int y, unsigne 187 | for(i = len; i && i > len - 3; buf[--i] = '.'); 188 | 189 | if (render) { 190 | - th = curfont->ascent + curfont->descent; 191 | - ty = y + (h / 2) - (th / 2) + curfont->ascent; 192 | + /* th = pad ? (curfont->ascent + curfont->descent) : 0; */ 193 | + ty = y + ((h + curfont->ascent - curfont->descent) / 2); 194 | tx = x + (h / 2); 195 | - XftDrawStringUtf8(d, invert ? &drw->scheme->bg->rgb : &drw->scheme->fg->rgb, curfont->xfont, tx, ty, (XftChar8 *)buf, len); 196 | + XftDrawStringUtf8(d, &drw->scheme->fg->rgb, curfont->xfont, tx, ty, (XftChar8 *)buf, len); 197 | } 198 | 199 | x += tex.w; 200 | -------------------------------------------------------------------------------- /patches/13_systray.patch: -------------------------------------------------------------------------------- 1 | --- dwm.c.orig 2015-06-21 17:19:52.935903099 +0100 2 | +++ dwm.c 2015-06-21 17:25:31.636382684 +0100 3 | @@ -65,12 +65,31 @@ 4 | #define TEXTW(X) (drw_text(drw, 0, 0, 0, 0, (X), 0) + drw->fonts[0]->h) 5 | #define TRUNC(X,A,B) (MAX((A), MIN((X), (B)))) 6 | 7 | +#define SYSTEM_TRAY_REQUEST_DOCK 0 8 | +#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0 9 | + 10 | +/* XEMBED messages */ 11 | +#define XEMBED_EMBEDDED_NOTIFY 0 12 | +#define XEMBED_WINDOW_ACTIVATE 1 13 | +#define XEMBED_FOCUS_IN 4 14 | +#define XEMBED_MODALITY_ON 10 15 | + 16 | +#define XEMBED_MAPPED (1 << 0) 17 | +#define XEMBED_WINDOW_ACTIVATE 1 18 | +#define XEMBED_WINDOW_DEACTIVATE 2 19 | + 20 | +#define VERSION_MAJOR 0 21 | +#define VERSION_MINOR 0 22 | +#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR 23 | + 24 | /* enums */ 25 | enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ 26 | enum { SchemeNorm, SchemeSel, SchemeLast }; /* color schemes */ 27 | enum { NetSupported, NetWMName, NetWMState, 28 | NetWMFullscreen, NetActiveWindow, NetWMWindowType, 29 | - NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ 30 | + NetWMWindowTypeDialog, NetClientList, NetSystemTray, 31 | + NetSystemTrayOP, NetSystemTrayOrientation, NetLast }; /* EWMH atoms */ 32 | +enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ 33 | enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ 34 | enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkClientWin, 35 | ClkRootWin, ClkLast }; /* clicks */ 36 | @@ -160,6 +179,12 @@ typedef struct { 37 | int monitor; 38 | } Rule; 39 | 40 | +typedef struct Systray Systray; 41 | +struct Systray { 42 | + Window win; 43 | + Client *icons; 44 | +}; 45 | + 46 | /* function declarations */ 47 | static void applyrules(Client *c); 48 | static Bool applysizehints(Client *c, int *x, int *y, int *w, int *h, Bool interact); 49 | @@ -217,7 +242,7 @@ static void restack(Monitor *m); 50 | static void run(void); 51 | static void runorraise(const Arg *arg); 52 | static void scan(void); 53 | -static Bool sendevent(Client *c, Atom proto); 54 | +static Bool sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4); 55 | static void self_restart(const Arg *arg); 56 | static void sendmon(Client *c, Monitor *m); 57 | static void setclientstate(Client *c, long state); 58 | @@ -258,6 +283,15 @@ static int xerrordummy(Display *dpy, XEr 59 | static int xerrorstart(Display *dpy, XErrorEvent *ee); 60 | static void zoom(const Arg *arg); 61 | static void bstack(Monitor *m); 62 | +static Atom getatomprop(Client *c, Atom prop); 63 | +static unsigned int getsystraywidth(); 64 | +static void removesystrayicon(Client *i); 65 | +static void resizebarwin(Monitor *m); 66 | +static void resizerequest(XEvent *e); 67 | +static void updatesystray(void); 68 | +static void updatesystrayicongeom(Client *i, int w, int h); 69 | +static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); 70 | +static Client *wintosystrayicon(Window w); 71 | 72 | /* variables */ 73 | static const char broken[] = "broken"; 74 | @@ -281,9 +315,10 @@ static void (*handler[LASTEvent]) (XEven 75 | [MapRequest] = maprequest, 76 | [MotionNotify] = motionnotify, 77 | [PropertyNotify] = propertynotify, 78 | + [ResizeRequest] = resizerequest, 79 | [UnmapNotify] = unmapnotify 80 | }; 81 | -static Atom wmatom[WMLast], netatom[NetLast]; 82 | +static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; 83 | static Bool running = True; 84 | static Cur *cursor[CurLast]; 85 | static ClrScheme scheme[MAXCOLORS]; 86 | @@ -292,6 +327,8 @@ static Drw *drw; 87 | static Monitor *mons, *selmon; 88 | static Window root; 89 | static int gap; 90 | +static Systray *systray = NULL; 91 | +static unsigned long systrayorientation = _NET_SYSTEM_TRAY_ORIENTATION_HORZ; 92 | 93 | /* configuration, allows nested code to access above variables */ 94 | #include "config.h" 95 | @@ -537,6 +574,10 @@ cleanup(void) { 96 | XUngrabKey(dpy, AnyKey, AnyModifier, root); 97 | while(mons) 98 | cleanupmon(mons); 99 | + if(showsystray) { 100 | + XUnmapWindow(dpy, systray->win); 101 | + free(systray); 102 | + } 103 | drw_cur_free(drw, cursor[CurNormal]); 104 | drw_cur_free(drw, cursor[CurResize]); 105 | drw_cur_free(drw, cursor[CurMove]); 106 | @@ -584,9 +625,49 @@ clearurgent(Client *c) { 107 | 108 | void 109 | clientmessage(XEvent *e) { 110 | + XWindowAttributes wa; 111 | + XSetWindowAttributes swa; 112 | XClientMessageEvent *cme = &e->xclient; 113 | Client *c = wintoclient(cme->window); 114 | 115 | + if(showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) { 116 | + /* add systray icons */ 117 | + if(cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { 118 | + if(!(c = (Client *)calloc(1, sizeof(Client)))) 119 | + die("fatal: could not malloc() %u bytes\n", sizeof(Client)); 120 | + c->win = cme->data.l[2]; 121 | + c->mon = selmon; 122 | + c->next = systray->icons; 123 | + systray->icons = c; 124 | + XGetWindowAttributes(dpy, c->win, &wa); 125 | + c->x = c->oldx = c->y = c->oldy = 0; 126 | + c->w = c->oldw = wa.width; 127 | + c->h = c->oldh = wa.height; 128 | + c->oldbw = wa.border_width; 129 | + c->bw = 0; 130 | + c->isfloating = True; 131 | + /* reuse tags field as mapped status */ 132 | + c->tags = 1; 133 | + updatesizehints(c); 134 | + updatesystrayicongeom(c, wa.width, wa.height); 135 | + XAddToSaveSet(dpy, c->win); 136 | + XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask); 137 | + XReparentWindow(dpy, c->win, systray->win, 0, 0); 138 | + /* use parents background pixmap */ 139 | + swa.background_pixmap = ParentRelative; 140 | + XChangeWindowAttributes(dpy, c->win, CWBackPixmap, &swa); 141 | + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 142 | + /* FIXME not sure if I have to send these events, too */ 143 | + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 144 | + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 145 | + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 146 | + resizebarwin(selmon); 147 | + updatesystray(); 148 | + setclientstate(c, NormalState); 149 | + } 150 | + return; 151 | + } 152 | + 153 | if(!c) 154 | return; 155 | if(cme->message_type == netatom[NetWMState]) { 156 | @@ -636,7 +717,7 @@ configurenotify(XEvent *e) { 157 | drw_resize(drw, sw, bh); 158 | updatebars(); 159 | for(m = mons; m; m = m->next) 160 | - XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); 161 | + resizebarwin(m); 162 | focus(NULL); 163 | arrange(NULL); 164 | } 165 | @@ -734,6 +815,11 @@ destroynotify(XEvent *e) { 166 | 167 | if((c = wintoclient(ev->window))) 168 | unmanage(c, True); 169 | + else if((c = wintosystrayicon(ev->window))) { 170 | + removesystrayicon(c); 171 | + resizebarwin(selmon); 172 | + updatesystray(); 173 | + } 174 | } 175 | 176 | void 177 | @@ -779,6 +865,7 @@ drawbar(Monitor *m) { 178 | char posbuf[10]; 179 | Client *c; 180 | 181 | + resizebarwin(m); 182 | for(c = m->clients; c; c = c->next) { 183 | occ |= c->tags; 184 | if(c->isurgent) 185 | @@ -810,6 +897,9 @@ drawbar(Monitor *m) { 186 | if(m == selmon) { /* status is only drawn on selected monitor */ 187 | w = TEXTW(stext); 188 | x = m->ww - w; 189 | + if(systray) { 190 | + x -= getsystraywidth(); 191 | + } 192 | if(x < xx) { 193 | x = xx; 194 | w = m->ww - xx; 195 | @@ -833,6 +923,7 @@ drawbars(void) { 196 | 197 | for(m = mons; m; m = m->next) 198 | drawbar(m); 199 | + updatesystray(); 200 | } 201 | 202 | void 203 | @@ -931,10 +1022,17 @@ getatomprop(Client *c, Atom prop) { 204 | unsigned long dl; 205 | unsigned char *p = NULL; 206 | Atom da, atom = None; 207 | + /* FIXME getatomprop should return the number of items and a pointer to 208 | + * the stored data instead of this workaround */ 209 | + Atom req = XA_ATOM; 210 | + if(prop == xatom[XembedInfo]) 211 | + req = xatom[XembedInfo]; 212 | 213 | - if(XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, 214 | + if(XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, 215 | &da, &di, &dl, &dl, &p) == Success && p) { 216 | atom = *(Atom *)p; 217 | + if(da == xatom[XembedInfo] && dl == 2) 218 | + atom = ((Atom *)p)[1]; 219 | XFree(p); 220 | } 221 | return atom; 222 | @@ -1066,7 +1164,7 @@ void 223 | killclient(const Arg *arg) { 224 | if(!selmon->sel) 225 | return; 226 | - if(!sendevent(selmon->sel, wmatom[WMDelete])) { 227 | + if(!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) { 228 | XGrabServer(dpy); 229 | XSetErrorHandler(xerrordummy); 230 | XSetCloseDownMode(dpy, DestroyAll); 231 | @@ -1165,7 +1263,13 @@ void 232 | maprequest(XEvent *e) { 233 | static XWindowAttributes wa; 234 | XMapRequestEvent *ev = &e->xmaprequest; 235 | + Client *i; 236 | 237 | + if((i = wintosystrayicon(ev->window))) { 238 | + sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); 239 | + resizebarwin(selmon); 240 | + updatesystray(); 241 | + } 242 | if(!XGetWindowAttributes(dpy, ev->window, &wa)) 243 | return; 244 | if(wa.override_redirect) 245 | @@ -1284,6 +1388,16 @@ propertynotify(XEvent *e) { 246 | Window trans; 247 | XPropertyEvent *ev = &e->xproperty; 248 | 249 | + if((c = wintosystrayicon(ev->window))) { 250 | + if(ev->atom == XA_WM_NORMAL_HINTS) { 251 | + updatesizehints(c); 252 | + updatesystrayicongeom(c, c->w, c->h); 253 | + } 254 | + else 255 | + updatesystrayiconstate(c, ev); 256 | + resizebarwin(selmon); 257 | + updatesystray(); 258 | + } 259 | if((ev->window == root) && (ev->atom == XA_WM_NAME)) 260 | updatestatus(); 261 | else if(ev->state == PropertyDelete) 262 | @@ -1547,25 +1661,35 @@ setclientstate(Client *c, long state) { 263 | } 264 | 265 | Bool 266 | -sendevent(Client *c, Atom proto) { 267 | +sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) { 268 | int n; 269 | - Atom *protocols; 270 | + Atom *protocols, mt; 271 | Bool exists = False; 272 | XEvent ev; 273 | 274 | - if(XGetWMProtocols(dpy, c->win, &protocols, &n)) { 275 | - while(!exists && n--) 276 | - exists = protocols[n] == proto; 277 | - XFree(protocols); 278 | + if(proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { 279 | + mt = wmatom[WMProtocols]; 280 | + if(XGetWMProtocols(dpy, w, &protocols, &n)) { 281 | + while(!exists && n--) 282 | + exists = protocols[n] == proto; 283 | + XFree(protocols); 284 | + } 285 | + } 286 | + else { 287 | + exists = True; 288 | + mt = proto; 289 | } 290 | if(exists) { 291 | ev.type = ClientMessage; 292 | - ev.xclient.window = c->win; 293 | - ev.xclient.message_type = wmatom[WMProtocols]; 294 | + ev.xclient.window = w; 295 | + ev.xclient.message_type = mt; 296 | ev.xclient.format = 32; 297 | - ev.xclient.data.l[0] = proto; 298 | - ev.xclient.data.l[1] = CurrentTime; 299 | - XSendEvent(dpy, c->win, False, NoEventMask, &ev); 300 | + ev.xclient.data.l[0] = d0; 301 | + ev.xclient.data.l[1] = d1; 302 | + ev.xclient.data.l[2] = d2; 303 | + ev.xclient.data.l[3] = d3; 304 | + ev.xclient.data.l[4] = d4; 305 | + XSendEvent(dpy, w, False, mask, &ev); 306 | } 307 | return exists; 308 | } 309 | @@ -1578,7 +1702,7 @@ setfocus(Client *c) { 310 | XA_WINDOW, 32, PropModeReplace, 311 | (unsigned char *) &(c->win), 1); 312 | } 313 | - sendevent(c, wmatom[WMTakeFocus]); 314 | + sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); 315 | } 316 | 317 | void 318 | @@ -1667,6 +1791,12 @@ setup(void) { 319 | netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); 320 | netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); 321 | netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); 322 | + netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); 323 | + netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); 324 | + netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); 325 | + xatom[Manager] = XInternAtom(dpy, "MANAGER", False); 326 | + xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); 327 | + xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); 328 | /* init cursors */ 329 | cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); 330 | cursor[CurResize] = drw_cur_create(drw, XC_sizing); 331 | @@ -1677,7 +1807,8 @@ setup(void) { 332 | scheme[i].fg = drw_clr_create(drw, colors[i][1]); 333 | scheme[i].bg = drw_clr_create(drw, colors[i][2]); 334 | } 335 | - 336 | + /* init system tray */ 337 | + updatesystray(); 338 | /* init bars */ 339 | updatebars(); 340 | updatestatus(); 341 | @@ -1809,7 +1940,18 @@ void 342 | togglebar(const Arg *arg) { 343 | selmon->showbar = !selmon->showbar; 344 | updatebarpos(selmon); 345 | - XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); 346 | + resizebarwin(selmon); 347 | + if(showsystray) { 348 | + XWindowChanges wc; 349 | + if(!selmon->showbar) 350 | + wc.y = -bh; 351 | + else if(selmon->showbar) { 352 | + wc.y = 0; 353 | + if(!selmon->topbar) 354 | + wc.y = selmon->mh - bh; 355 | + } 356 | + XConfigureWindow(dpy, systray->win, CWY, &wc); 357 | + } 358 | arrange(selmon); 359 | } 360 | 361 | @@ -1910,10 +2052,16 @@ unmapnotify(XEvent *e) { 362 | else 363 | unmanage(c, False); 364 | } 365 | + else if((c = wintosystrayicon(ev->window))) { 366 | + removesystrayicon(c); 367 | + resizebarwin(selmon); 368 | + updatesystray(); 369 | + } 370 | } 371 | 372 | void 373 | updatebars(void) { 374 | + unsigned int w; 375 | Monitor *m; 376 | XSetWindowAttributes wa = { 377 | .override_redirect = True, 378 | @@ -1923,7 +2071,10 @@ updatebars(void) { 379 | for(m = mons; m; m = m->next) { 380 | if (m->barwin) 381 | continue; 382 | - m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), 383 | + w = m->ww; 384 | + if(showsystray && m == selmon) 385 | + w -= getsystraywidth(); 386 | + m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen), 387 | CopyFromParent, DefaultVisual(dpy, screen), 388 | CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); 389 | XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); 390 | @@ -2276,6 +2427,158 @@ bstack(Monitor *m) { 391 | } 392 | } 393 | 394 | +unsigned int 395 | +getsystraywidth() { 396 | + unsigned int w = 0; 397 | + Client *i; 398 | + if(showsystray) 399 | + for(i = systray->icons; i; w += i->w + 1, i = i->next) ; 400 | + return w ? w + 1 : 1; 401 | +} 402 | + 403 | +void 404 | +removesystrayicon(Client *i) { 405 | + Client **ii; 406 | + 407 | + if(!showsystray || !i) 408 | + return; 409 | + for(ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next); 410 | + if(ii) 411 | + *ii = i->next; 412 | + free(i); 413 | +} 414 | + 415 | +void 416 | +resizebarwin(Monitor *m) { 417 | + unsigned int w = m->ww; 418 | + if(showsystray && m == selmon) 419 | + w -= getsystraywidth(); 420 | + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh); 421 | +} 422 | + 423 | +void 424 | +resizerequest(XEvent *e) { 425 | + XResizeRequestEvent *ev = &e->xresizerequest; 426 | + Client *i; 427 | + 428 | + if((i = wintosystrayicon(ev->window))) { 429 | + updatesystrayicongeom(i, ev->width, ev->height); 430 | + resizebarwin(selmon); 431 | + updatesystray(); 432 | + } 433 | +} 434 | + 435 | +void 436 | +updatesystrayicongeom(Client *i, int w, int h) { 437 | + if(i) { 438 | + i->h = bh; 439 | + if(w == h) 440 | + i->w = bh; 441 | + else if(h == bh) 442 | + i->w = w; 443 | + else 444 | + i->w = (int) ((float)bh * ((float)w / (float)h)); 445 | + applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False); 446 | + /* force icons into the systray dimenons if they don't want to */ 447 | + if(i->h > bh) { 448 | + if(i->w == i->h) 449 | + i->w = bh; 450 | + else 451 | + i->w = (int) ((float)bh * ((float)i->w / (float)i->h)); 452 | + i->h = bh; 453 | + } 454 | + } 455 | +} 456 | + 457 | +void 458 | +updatesystrayiconstate(Client *i, XPropertyEvent *ev) { 459 | + long flags; 460 | + int code = 0; 461 | + 462 | + if(!showsystray || !i || ev->atom != xatom[XembedInfo] || 463 | + !(flags = getatomprop(i, xatom[XembedInfo]))) 464 | + return; 465 | + 466 | + if(flags & XEMBED_MAPPED && !i->tags) { 467 | + i->tags = 1; 468 | + code = XEMBED_WINDOW_ACTIVATE; 469 | + XMapRaised(dpy, i->win); 470 | + setclientstate(i, NormalState); 471 | + } 472 | + else if(!(flags & XEMBED_MAPPED) && i->tags) { 473 | + i->tags = 0; 474 | + code = XEMBED_WINDOW_DEACTIVATE; 475 | + XUnmapWindow(dpy, i->win); 476 | + setclientstate(i, WithdrawnState); 477 | + } 478 | + else 479 | + return; 480 | + sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0, 481 | + systray->win, XEMBED_EMBEDDED_VERSION); 482 | +} 483 | + 484 | +void 485 | +updatesystray(void) { 486 | + XSetWindowAttributes wa; 487 | + Client *i; 488 | + unsigned int x = selmon->mx + selmon->mw; 489 | + unsigned int w = 1; 490 | + ClrScheme *old = drw->scheme; 491 | + 492 | + if(!showsystray) 493 | + return; 494 | + if(!systray) { 495 | + /* init systray */ 496 | + drw_setscheme(drw, &scheme[0]); 497 | + if(!(systray = (Systray *)calloc(1, sizeof(Systray)))) 498 | + die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); 499 | + systray->win = XCreateSimpleWindow(dpy, root, x, selmon->by, w, bh, 0, 0, drw->scheme->bg->pix); 500 | + wa.event_mask = ButtonPressMask | ExposureMask; 501 | + wa.override_redirect = True; 502 | + wa.background_pixmap = ParentRelative; 503 | + wa.background_pixel = drw->scheme->bg->pix; 504 | + XSelectInput(dpy, systray->win, SubstructureNotifyMask); 505 | + XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32, 506 | + PropModeReplace, (unsigned char *)&systrayorientation, 1); 507 | + XChangeWindowAttributes(dpy, systray->win, CWEventMask | CWOverrideRedirect | CWBackPixel, &wa); 508 | + XMapRaised(dpy, systray->win); 509 | + XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime); 510 | + if(XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) { 511 | + sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0); 512 | + XSync(dpy, False); 513 | + } 514 | + else { 515 | + fprintf(stderr, "dwm: unable to obtain system tray.\n"); 516 | + free(systray); 517 | + systray = NULL; 518 | + return; 519 | + } 520 | + drw_setscheme(drw, old); 521 | + } 522 | + for(w = 0, i = systray->icons; i; i = i->next) { 523 | + XMapRaised(dpy, i->win); 524 | + w += 1; 525 | + XMoveResizeWindow(dpy, i->win, (i->x = w), 0, i->w, i->h); 526 | + w += i->w; 527 | + if(i->mon != selmon) 528 | + i->mon = selmon; 529 | + } 530 | + w = w ? w + 1 : 1; 531 | + x -= w; 532 | + XMoveResizeWindow(dpy, systray->win, x, selmon->by, w, bh); 533 | + XSync(dpy, False); 534 | +} 535 | + 536 | +Client * 537 | +wintosystrayicon(Window w) { 538 | + Client *i = NULL; 539 | + 540 | + if(!showsystray || !w) 541 | + return i; 542 | + for(i = systray->icons; i && i->win != w; i = i->next) ; 543 | + return i; 544 | +} 545 | + 546 | int 547 | main(int argc, char *argv[]) { 548 | if(argc == 2 && !strcmp("-v", argv[1])) 549 | -------------------------------------------------------------------------------- /patches/14_fifo.patch: -------------------------------------------------------------------------------- 1 | --- dwm.c 2016-09-05 19:28:47.229302576 +0100 2 | +++ dwm.c.new 2016-09-05 19:28:45.089310729 +0100 3 | @@ -21,6 +21,7 @@ 4 | * To understand everything else, start reading main(). 5 | */ 6 | #include 7 | +#include 8 | #include 9 | #include 10 | #include 11 | @@ -28,6 +29,8 @@ 12 | #include 13 | #include 14 | #include 15 | +#include 16 | +#include 17 | #include 18 | #include 19 | #include 20 | @@ -186,6 +189,12 @@ struct Systray { 21 | Client *icons; 22 | }; 23 | 24 | +typedef struct { 25 | + const char *name; 26 | + void (*func)(const Arg *arg); 27 | + const Arg arg; 28 | +} Command; 29 | + 30 | /* function declarations */ 31 | static void applyrules(Client *c); 32 | static Bool applysizehints(Client *c, int *x, int *y, int *w, int *h, Bool interact); 33 | @@ -208,9 +217,11 @@ static void destroynotify(XEvent *e); 34 | static void detach(Client *c); 35 | static void detachstack(Client *c); 36 | static Monitor *dirtomon(int dir); 37 | +static void dispatchcmd(void); 38 | static void drawbar(Monitor *m); 39 | static void drawbars(void); 40 | static void enternotify(XEvent *e); 41 | +static Bool evpredicate(); 42 | static void expose(XEvent *e); 43 | static void focus(Client *c); 44 | static void focusin(XEvent *e); 45 | @@ -330,6 +341,7 @@ static Window root; 46 | static int gap; 47 | static Systray *systray = NULL; 48 | static unsigned long systrayorientation = _NET_SYSTEM_TRAY_ORIENTATION_HORZ; 49 | +static int fifofd; 50 | 51 | /* configuration, allows nested code to access above variables */ 52 | #include "config.h" 53 | @@ -592,6 +604,7 @@ cleanup(void) { 54 | XSync(dpy, False); 55 | XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); 56 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); 57 | + close(fifofd); 58 | } 59 | 60 | void 61 | @@ -859,6 +872,31 @@ dirtomon(int dir) { 62 | return m; 63 | } 64 | 65 | +Bool 66 | +evpredicate() 67 | +{ 68 | + return True; 69 | +} 70 | + 71 | +void 72 | +dispatchcmd(void) 73 | +{ 74 | + int i; 75 | + char buf[BUFSIZ]; 76 | + ssize_t n; 77 | + 78 | + n = read(fifofd, buf, sizeof(buf) - 1); 79 | + if (n == -1) 80 | + die("Failed to read() from DWM fifo %s:", dwmfifo); 81 | + buf[n] = '\0'; 82 | + for (i = 0; i < LENGTH(commands); i++) { 83 | + if (strcmp(commands[i].name, buf) == 0) { 84 | + commands[i].func(&commands[i].arg); 85 | + break; 86 | + } 87 | + } 88 | +} 89 | + 90 | void 91 | drawbar(Monitor *m) { 92 | int x, xx, w; 93 | @@ -1574,10 +1612,30 @@ void 94 | run(void) { 95 | XEvent ev; 96 | /* main event loop */ 97 | - XSync(dpy, False); 98 | - while(running && !XNextEvent(dpy, &ev)) 99 | - if(handler[ev.type]) 100 | - handler[ev.type](&ev); /* call handler */ 101 | + fd_set rfds; 102 | + int n; 103 | + int dpyfd, maxfd; 104 | + /* main event loop */ 105 | + XSync(dpy, False); 106 | + dpyfd = ConnectionNumber(dpy); 107 | + maxfd = fifofd; 108 | + if (dpyfd > maxfd) 109 | + maxfd = dpyfd; 110 | + maxfd++; 111 | + while (running) { 112 | + FD_ZERO(&rfds); 113 | + FD_SET(fifofd, &rfds); 114 | + FD_SET(dpyfd, &rfds); 115 | + n = select(maxfd, &rfds, NULL, NULL, NULL); 116 | + if (n > 0) { 117 | + if (FD_ISSET(fifofd, &rfds)) 118 | + dispatchcmd(); 119 | + if (FD_ISSET(dpyfd, &rfds)) 120 | + while (XCheckIfEvent(dpy, &ev, evpredicate, NULL)) 121 | + if (handler[ev.type]) 122 | + handler[ev.type](&ev); /* call handler */ 123 | + } 124 | + } 125 | } 126 | 127 | void 128 | @@ -1827,6 +1885,9 @@ setup(void) { 129 | XSelectInput(dpy, root, wa.event_mask); 130 | grabkeys(); 131 | focus(NULL); 132 | + fifofd = open(dwmfifo, O_RDWR | O_NONBLOCK); 133 | + if (fifofd < 0) 134 | + die("Failed to open() DWM fifo %s:", dwmfifo); 135 | } 136 | 137 | void 138 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | 6 | #include "util.h" 7 | 8 | void 9 | die(const char *errstr, ...) { 10 | va_list ap; 11 | 12 | va_start(ap, errstr); 13 | vfprintf(stderr, errstr, ap); 14 | va_end(ap); 15 | exit(EXIT_FAILURE); 16 | } 17 | 18 | -------------------------------------------------------------------------------- /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 | 7 | void die(const char *errstr, ...); 8 | --------------------------------------------------------------------------------