├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── autostart ├── autoscreen.sh └── autostart.sh ├── config.def.h ├── config.h ├── config.mk ├── drw.cpp ├── drw.h ├── dwm.1 ├── dwm.cpp ├── dwm.png ├── flake.lock ├── flake.nix ├── flextile-deluxe.c ├── flextile-deluxe.h ├── fonts ├── JoyPixels.ttf ├── Monaco Regular.ttf ├── SymbolsNerdFont-Regular.ttf └── chinese.msyh.ttf ├── i3lock └── lock.sh ├── patch ├── dwm-autostart-20161205-bb3bd6f.diff ├── dwm-cyclelayouts-20180524-6.2.diff ├── dwm-doublepressquit-6.3.diff ├── dwm-fancybar-20200423-ed3ab6b.diff ├── dwm-focusdir-6.3.diff ├── dwm-fullgaps-toggle-20200830.diff ├── dwm-gaplessgrid-20160731-56a31dc.diff ├── dwm-hide_vacant_tags-6.3.diff ├── dwm-launchers-20200527-f09418b.diff ├── dwm-pertag-flextile_deluxe-6.3.diff ├── dwm-pertag-perseltag-6.2.diff ├── dwm-restartsig-20180523-6.2.diff ├── dwm-restoreafterrestart-20220709-d3f93c7.diff ├── dwm-rotatestack-20161021-ab9571b.diff ├── dwm-scratchpad-6.2.diff ├── dwm-statuscmd-20210405-67d76bd.diff ├── dwm-systray-6.3.diff └── dwm-viewontag-20210312-61bb8b2.diff ├── statusbar ├── battery.py ├── bluetooth_battery.py ├── common.py ├── cpu.py ├── date.py ├── icon.py ├── memory.py ├── music_next.py ├── music_play.py ├── music_pre.py ├── music_title.py ├── net.py ├── pacman.py ├── screen.py ├── statusbar.py ├── vol.py └── wifi.py ├── themes ├── catppuccin.h ├── dracula.h ├── gruvchad.h ├── nord.h └── onedark.h ├── transient.c ├── util.cpp └── util.h /.gitignore: -------------------------------------------------------------------------------- 1 | dwm 2 | *.o 3 | .ccls-cache/ 4 | compile_commands.json 5 | .cache 6 | statusbar/temp 7 | result/ 8 | *__pycache__* 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT/X Consortium License 2 | 3 | © 2006-2019 Anselm R Garbe 4 | © 2006-2009 Jukka Salmi 5 | © 2006-2007 Sander van Dijk 6 | © 2007-2011 Peter Hartlich 7 | © 2007-2009 Szabolcs Nagy 8 | © 2007-2009 Christof Musik 9 | © 2007-2009 Premysl Hruby 10 | © 2007-2008 Enno Gottox Boland 11 | © 2008 Martin Hurton 12 | © 2008 Neale Pickett 13 | © 2009 Mate Nagy 14 | © 2010-2016 Hiltjo Posthuma 15 | © 2010-2012 Connor Lane Smith 16 | © 2011 Christoph Lohmann <20h@r-36.net> 17 | © 2015-2016 Quentin Rameau 18 | © 2015-2016 Eric Pruitt 19 | © 2016-2017 Markus Teich 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a 22 | copy of this software and associated documentation files (the "Software"), 23 | to deal in the Software without restriction, including without limitation 24 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 25 | and/or sell copies of the Software, and to permit persons to whom the 26 | Software is furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in 29 | all copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 34 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 36 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 37 | DEALINGS IN THE SOFTWARE. 38 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # dwm - dynamic window manager 2 | # See LICENSE file for copyright and license details. 3 | 4 | include config.mk 5 | 6 | SRC = drw.cpp dwm.cpp util.cpp 7 | OBJ = ${SRC:.cpp=.o} 8 | 9 | all: options dwm 10 | 11 | options: 12 | @echo dwm build options: 13 | @echo "CFLAGS = ${CFLAGS}" 14 | @echo "CXXFLAGS = ${CXXFLAGS}" 15 | @echo "LDFLAGS = ${LDFLAGS}" 16 | @echo "CC = ${CC}" 17 | @echo "CXX = ${CXX}" 18 | 19 | .cpp.o: 20 | ${CXX} -c ${CXXFLAGS} $< 21 | 22 | .c.o: 23 | ${CXX} -c ${CXXFLAGS} $< 24 | 25 | ${OBJ}: config.h config.mk 26 | 27 | config.h: 28 | cp config.def.h $@ 29 | 30 | dwm: ${OBJ} 31 | ${CC} -o $@ ${OBJ} ${LDFLAGS} 32 | 33 | clean: 34 | rm -f ${OBJ} dwm-${VERSION}.tar.gz config.h dwm drw.o dwm.o util.o *.orig *.rej 35 | 36 | dist: clean 37 | mkdir -p dwm-${VERSION} 38 | cp -R LICENSE Makefile README config.def.h config.mk\ 39 | dwm.1 drw.h util.h ${SRC} dwm.png transient.c 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 | mkdir -p ${DESTDIR}${PREFIX}/bin 46 | cp -f dwm ${DESTDIR}${PREFIX}/bin 47 | chmod 755 ${DESTDIR}${PREFIX}/bin/dwm 48 | mkdir -p ${DESTDIR}${MANPREFIX}/man1 49 | sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1 50 | chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1 51 | 52 | uninstall: 53 | rm -f ${DESTDIR}${PREFIX}/bin/dwm\ 54 | ${DESTDIR}${MANPREFIX}/man1/dwm.1 55 | 56 | .PHONY: all options clean dist install uninstall 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DWM 2 | 3 | b站简单演示链接:[https://www.bilibili.com/video/BV1hY4y127ef](https://www.bilibili.com/video/BV1hY4y127ef) 4 | 5 | 安装教程视频:https://www.bilibili.com/video/BV1co4y1z7BT 6 | 7 | 8 | 9 | **重要的事情说三遍,大多数人碰到过字体问题,为了不再麻烦大家自己找字体,已经把我用到的所有字体放到了fonts目录下,记得复制到`/usr/share/fonts/`目录下** 10 | 11 | **重要的事情说三遍,大多数人碰到过字体问题,为了不再麻烦大家自己找字体,已经把我用到的所有字体放到了fonts目录下,记得复制到`/usr/share/fonts/`目录下** 12 | 13 | **重要的事情说三遍,大多数人碰到过字体问题,为了不再麻烦大家自己找字体,已经把我用到的所有字体放到了fonts目录下,记得复制到`/usr/share/fonts/`目录下** 14 | 15 | 16 | 17 | *** 18 | 19 | **如果你的觉得这个DWM还不错,麻烦点个star,谢谢! 这会是对我最大的激励** 20 | 21 | **如果你的觉得这个DWM还不错,麻烦点个star,谢谢! 这会是对我最大的激励** 22 | 23 | **如果你的觉得这个DWM还不错,麻烦点个star,谢谢! 这会是对我最大的激励** 24 | *** 25 | 26 | 本仓库主要 fork 自yaoccc https://github.com/yaocccc/dwm 并修改了自己需要的内容 27 | 28 | 首先要感谢yaoccc的dwm,主要基于`e49d3b8`历史版本进行了修改 29 | 30 | 31 | ## 说在前面: 32 | 33 | **dwm安装视频教程看最上面链接** 34 | 35 | 1. 遇到安装问题可以直接提issue或者b站底下评论。 36 | 37 | 2. 遇到linux相关问题请优先自己搜索,没法解决再提问,和本仓库相关建议直接提问,没有必要试错 38 | 39 | 3. 如果你现在在用dwm,想做一些增强但是没法实现,也可以直接和我说,如果我觉得想法很精彩,在力所能及的范围内也会主动去做的 40 | 41 | 4. 复杂问题没法简单说的可以联系邮箱`gxt_kt@163.com`,或者访问[http://gxt-kt.cn/](http://gxt-kt.cn/)留言 42 | 43 | 44 | 45 | ## 本仓库修改内容 46 | 47 | **相比原版修改主要内容为:** 48 | 49 | > 1. 增加功能: win+hjkl可以直接聚焦窗口,和vim,tmux的操作逻辑类似. 50 | > 51 | > 原生的是一维聚焦窗口,现在改成二维聚焦,更符合操作直觉 52 | > 53 | > 2. 增加功能:win+shift+hjkl二维交换窗口 54 | > 55 | > 3. 全部重构statusbar.sh实现,全部采用python实现 56 | > 57 | > 采用python+多线程,使用多线程优化多子任务效率,可以实现同步多子任务1s刷新 58 | > 59 | > 2022-02-27 再次优化了多线程执行,基本可以保证时间准确. 60 | > 61 | > 4. 增加功能:增加可以不允许普通kill掉程序 (仍然允许使用forcekill关闭程序) 62 | > 63 | > 比如使用tmux打开很多终端,为了防止手贱误关闭程序,可以把tmux加入到不允许普通kill保护中 64 | > 65 | > 又或者打开腾讯会议共享桌面,开会等关键时刻,防止手贱把腾讯会议关了等等使用场景 66 | > 67 | > 5. 增加功能:原生支持键盘操作音量,屏幕亮度调整等 68 | > 69 | > 6. 增加功能:可以切换历史tag,可以自定义追溯历史tag大小 70 | > 71 | > 如果特别设置一下,可以在任意两个tag之间一键来回切换 72 | > 73 | > 这个功能刚开始用也许感觉很鸡肋,后面适应了会感觉和vim二维操作一样,离不开了 74 | > 75 | > 6. 增加功能:优化热重启,在补丁基础上再次优化,不会重复执行autostart 76 | > 77 | > 7. 增加了一些补丁,并选择保留补丁特性,在补丁基础上进一步优化,包括但不限于: 78 | > 79 | > - 连续两次激活按键关闭dwm才进行关闭,防止误触 80 | > - 热重启dwm 更改配置文件重新编译安装后可以直接重启dwm并保留当前已经打开窗口和布局 81 | > - 旋转堆栈 可以更改窗口显示顺序 (已经注释,需要手动启用) 82 | > - 添加flextile增强布局 83 | 84 | 85 | **其它一些优化内容为:** 86 | > 1. 增加右侧tile布局(需要手动启用,如果使用flex增强布局则已经默认启用) 87 | > 88 | > 原版为左侧tile布局,增加右侧布局符合某些操作习惯 89 | > 90 | > 2. 彻底修复窗口隐藏和修复的bug 91 | > 92 | > 之前有提过pr,但是后面发现特殊情况下scratchpad仍然会打断窗口恢复,现在这个仓库已经彻底修复,等有时间再去yaoccc那提个pr修复一下 93 | > 94 | > 3. 解决tag下没有窗口除了scratchpad仍然显示tag的bug 95 | > 96 | > 比如tag2下打开scratchpad,但tag2没有别的其它窗口,状态栏仍然会显示tag2的图标 97 | 98 | **其他一些次要修改内容为:** 99 | 100 | > 1. 终端使用全部使用Alacritty,原版为st 101 | > 2. 按键由于hjkl优化,改动较大,建议熟悉vim使用 102 | 103 | 104 | ## 使用注意事项 105 | 106 | 1. config.def.h 文件中调用一些,确保这些命令存在就行,(不存在也可以正常运行,只是缺少对应功能) 107 | 108 | 测试方法为:手动复制命令到终端中执行,如果成功就没问题 109 | 110 | 2. statusbar.py 文件中根据需要注释相应的包,确保要使用的包pip安装了 111 | 112 | 3. 我自己dwm的路径为 `~/my_desktop/dwm` , 相关功能的启用需要修改到你们自己的路径. 113 | 114 | 建议也先将dwm安装到和我一样的目录,后续再更改对应路径到你自己的 115 | 116 | 117 | 118 | ## 目前本仓库一些可以优化的地方 119 | 120 | 1. ~~statusbar中音量功能中加入了一个显示蓝牙设备剩余电量功能,但是目前没法稳定使用,一般在刚开始连接时可以正常检测到,后面就不行. 这个暂时解决不了,只能说暂时不使用功能,需要等待上游更新或arch内核更新. (arch的蓝牙经常不稳定)~~ 121 | 122 | 已经修复,采用其它途径获取蓝牙音量,目前还比较稳定 123 | 124 | 125 | ## 一些其它值得讲的点 126 | 1. 对于一些特殊按键不知道名字,可以在终端内执行`xev`,就可以显示当前所按按键详细信息 127 | 比如`\`键叫做`XK_backclash` 128 | 129 | 2. 在statusbar中使用sudo没法输入密码,解决方法有很多种,不建议采用明文泄露密码,建议采用下面方法: 130 | 比如pacman执行sudo不需要输入密码,在`/etc/sudoers`加入`${user} ALL=(ALL) NOPASSWD: /usr/bin/pacman` 131 | 132 | 3. 建议在各种高危尝试前,先使用`timeshift`备份 133 | -------------------------------------------------------------------------------- /autostart/autoscreen.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | autorandr --change 4 | -------------------------------------------------------------------------------- /autostart/autostart.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | # If you find fcitx5 icon is located at the most left of the straybar, please increase the delay value 4 | sleep 1 # need to wait dwm start complete and fcitx5 start complete 5 | 6 | # Notice that cron need exec before other program 7 | cron() { 8 | let i=1 9 | while true; do 10 | [ $((i % 3)) -eq 0 ] && ~/my_desktop/dwm/autostart/autoscreen.sh # check screen and autoset 11 | sleep 1; let i+=1 12 | done 13 | } 14 | cron& 15 | 16 | 17 | cfw & # clash for windows 18 | crow & # translate 19 | blueman-manager & # bluetooth manager 20 | copyq & # copy software 21 | 22 | picom --experimental-backends& 23 | 24 | 25 | pkill -f statusbar.py 26 | python3 ~/my_desktop/dwm/statusbar/statusbar.py cron &>/dev/null 27 | 28 | 29 | libinput-gestures-setup start # touchpad open gesture 30 | xinput --set-prop 15 'libinput Accel Speed' 0.5 # set touchpad sensitivity 31 | 32 | xhost + # add support for docker gui app 33 | -------------------------------------------------------------------------------- /config.mk: -------------------------------------------------------------------------------- 1 | # dwm version 2 | VERSION = 6.3 3 | 4 | # Customize below to fit your system 5 | 6 | # paths 7 | PREFIX = /usr/local 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 | # freetype 18 | FREETYPELIBS = -lfontconfig -lXft 19 | FREETYPEINC = /usr/include/freetype2 20 | # OpenBSD (uncomment) 21 | #FREETYPEINC = ${X11INC}/freetype2 22 | 23 | # includes and libs 24 | INCS = -I${X11INC} -I${FREETYPEINC} 25 | LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lXrender 26 | 27 | # flags 28 | CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} 29 | # CPPFLAGS += -D_DEFAULT_SOURCE 30 | 31 | CXXFLAGS += -std=c++14 32 | CXXFLAGS += -pedantic 33 | CXXFLAGS += -Wall 34 | CXXFLAGS += -Wno-deprecated-declarations 35 | # CXXFLAGS += -Wno-unused-parameter 36 | CXXFLAGS += -g -O3 ${INCS} ${CPPFLAGS} 37 | 38 | # LDFLAGS = ${LIBS} 39 | LDFLAGS = -s ${LIBS} 40 | 41 | 42 | # compiler and linker 43 | # CC = cc 44 | CXX = g++ 45 | -------------------------------------------------------------------------------- /drw.cpp: -------------------------------------------------------------------------------- 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 | { 22 | for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) 23 | if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) 24 | return (unsigned char)c & ~utfmask[*i]; 25 | return 0; 26 | } 27 | 28 | static size_t 29 | utf8validate(long *u, size_t i) 30 | { 31 | if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) 32 | *u = UTF_INVALID; 33 | for (i = 1; *u > utfmax[i]; ++i) 34 | ; 35 | return i; 36 | } 37 | 38 | static size_t 39 | utf8decode(const char *c, long *u, size_t clen) 40 | { 41 | size_t i, j, len, type; 42 | long udecoded; 43 | 44 | *u = UTF_INVALID; 45 | if (!clen) 46 | return 0; 47 | udecoded = utf8decodebyte(c[0], &len); 48 | if (!BETWEEN(len, 1, UTF_SIZ)) 49 | return 1; 50 | for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { 51 | udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); 52 | if (type) 53 | return j; 54 | } 55 | if (j < len) 56 | return 0; 57 | *u = udecoded; 58 | utf8validate(u, len); 59 | 60 | return len; 61 | } 62 | 63 | Drw * 64 | drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap) 65 | { 66 | Drw *drw = (Drw*)ecalloc(1, sizeof(Drw)); 67 | 68 | drw->dpy = dpy; 69 | drw->screen = screen; 70 | drw->root = root; 71 | drw->w = w; 72 | drw->h = h; 73 | drw->visual = visual; 74 | drw->depth = depth; 75 | drw->cmap = cmap; 76 | drw->drawable = XCreatePixmap(dpy, root, w, h, depth); 77 | drw->gc = XCreateGC(dpy, drw->drawable, 0, NULL); 78 | XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); 79 | 80 | return drw; 81 | } 82 | 83 | void 84 | drw_resize(Drw *drw, unsigned int w, unsigned int h) 85 | { 86 | if (!drw) 87 | return; 88 | 89 | drw->w = w; 90 | drw->h = h; 91 | if (drw->drawable) 92 | XFreePixmap(drw->dpy, drw->drawable); 93 | drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, drw->depth); 94 | } 95 | 96 | void 97 | drw_free(Drw *drw) 98 | { 99 | XFreePixmap(drw->dpy, drw->drawable); 100 | XFreeGC(drw->dpy, drw->gc); 101 | drw_fontset_free(drw->fonts); 102 | free(drw); 103 | } 104 | 105 | /* This function is an implementation detail. Library users should use 106 | * drw_fontset_create instead. 107 | */ 108 | static Fnt * 109 | xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) 110 | { 111 | Fnt *font; 112 | XftFont *xfont = NULL; 113 | FcPattern *pattern = NULL; 114 | 115 | if (fontname) { 116 | /* Using the pattern found at font->xfont->pattern does not yield the 117 | * same substitution results as using the pattern returned by 118 | * FcNameParse; using the latter results in the desired fallback 119 | * behaviour whereas the former just results in missing-character 120 | * rectangles being drawn, at least with some fonts. */ 121 | if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { 122 | fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); 123 | return NULL; 124 | } 125 | if (!(pattern = FcNameParse((FcChar8 *) fontname))) { 126 | fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); 127 | XftFontClose(drw->dpy, xfont); 128 | return NULL; 129 | } 130 | } else if (fontpattern) { 131 | if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { 132 | fprintf(stderr, "error, cannot load font from pattern.\n"); 133 | return NULL; 134 | } 135 | } else { 136 | die("no font specified."); 137 | } 138 | 139 | font = (Fnt*)ecalloc(1, sizeof(Fnt)); 140 | font->xfont = xfont; 141 | font->pattern = pattern; 142 | font->h = xfont->ascent + xfont->descent; 143 | font->dpy = drw->dpy; 144 | 145 | return font; 146 | } 147 | 148 | static void 149 | xfont_free(Fnt *font) 150 | { 151 | if (!font) 152 | return; 153 | if (font->pattern) 154 | FcPatternDestroy(font->pattern); 155 | XftFontClose(font->dpy, font->xfont); 156 | free(font); 157 | } 158 | 159 | Fnt* 160 | drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) 161 | { 162 | Fnt *cur, *ret = NULL; 163 | size_t i; 164 | 165 | if (!drw || !fonts) 166 | return NULL; 167 | 168 | for (i = 1; i <= fontcount; i++) { 169 | if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { 170 | cur->next = ret; 171 | ret = cur; 172 | } 173 | } 174 | return (drw->fonts = ret); 175 | } 176 | 177 | void 178 | drw_fontset_free(Fnt *font) 179 | { 180 | if (font) { 181 | drw_fontset_free(font->next); 182 | xfont_free(font); 183 | } 184 | } 185 | 186 | void 187 | drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha) 188 | { 189 | if (!drw || !dest || !clrname) 190 | return; 191 | 192 | if (!XftColorAllocName(drw->dpy, drw->visual, drw->cmap, 193 | clrname, dest)) 194 | die("error, cannot allocate color '%s'", clrname); 195 | 196 | dest->pixel = (dest->pixel & 0x00ffffffU) | (alpha << 24); 197 | } 198 | 199 | /* Wrapper to create color schemes. The caller has to call free(3) on the 200 | * returned color scheme when done using it. */ 201 | Clr * 202 | drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount) 203 | { 204 | size_t i; 205 | Clr *ret; 206 | 207 | /* need at least two colors for a scheme */ 208 | if (!drw || !clrnames || clrcount < 2 || !(ret = (Clr*)ecalloc(clrcount, sizeof(XftColor)))) 209 | return NULL; 210 | 211 | for (i = 0; i < clrcount; i++) 212 | drw_clr_create(drw, &ret[i], clrnames[i], alphas[i]); 213 | return ret; 214 | } 215 | 216 | void 217 | drw_setfontset(Drw *drw, Fnt *set) 218 | { 219 | if (drw) 220 | drw->fonts = set; 221 | } 222 | 223 | void 224 | drw_setscheme(Drw *drw, Clr *scm) 225 | { 226 | if (drw) 227 | drw->scheme = scm; 228 | } 229 | 230 | void 231 | drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) 232 | { 233 | if (!drw || !drw->scheme) 234 | return; 235 | XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); 236 | if (filled) 237 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); 238 | else 239 | XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); 240 | } 241 | 242 | int 243 | drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) 244 | { 245 | char buf[1024]; 246 | int ty; 247 | unsigned int ew; 248 | XftDraw *d = NULL; 249 | Fnt *usedfont, *curfont, *nextfont; 250 | size_t i, len; 251 | int utf8strlen, utf8charlen, render = x || y || w || h; 252 | long utf8codepoint = 0; 253 | const char *utf8str; 254 | FcCharSet *fccharset; 255 | FcPattern *fcpattern; 256 | FcPattern *match; 257 | XftResult result; 258 | int charexists = 0; 259 | 260 | if (!drw || (render && !drw->scheme) || !text || !drw->fonts) 261 | return 0; 262 | 263 | if (!render) { 264 | w = ~w; 265 | } else { 266 | XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); 267 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); 268 | d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap); 269 | x += lpad; 270 | w -= lpad; 271 | } 272 | 273 | usedfont = drw->fonts; 274 | while (1) { 275 | ew = utf8strlen = 0; 276 | utf8str = text; 277 | nextfont = NULL; 278 | while (*text) { 279 | utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); 280 | for (curfont = drw->fonts; curfont; curfont = curfont->next) { 281 | charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); 282 | if (charexists) { 283 | if (curfont == usedfont) { 284 | utf8strlen += utf8charlen; 285 | text += utf8charlen; 286 | } else { 287 | nextfont = curfont; 288 | } 289 | break; 290 | } 291 | } 292 | 293 | if (!charexists || nextfont) 294 | break; 295 | else 296 | charexists = 0; 297 | } 298 | 299 | if (utf8strlen) { 300 | drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); 301 | /* shorten text if necessary */ 302 | for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--) 303 | drw_font_getexts(usedfont, utf8str, len, &ew, NULL); 304 | 305 | if (len) { 306 | memcpy(buf, utf8str, len); 307 | buf[len] = '\0'; 308 | if (len < utf8strlen) 309 | for (i = len; i && i > len - 3; buf[--i] = '.') 310 | ; /* NOP */ 311 | 312 | if (render) { 313 | ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; 314 | XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], 315 | usedfont->xfont, x, ty, (XftChar8 *)buf, len); 316 | } 317 | x += ew; 318 | w -= ew; 319 | } 320 | } 321 | 322 | if (!*text) { 323 | break; 324 | } else if (nextfont) { 325 | charexists = 0; 326 | usedfont = nextfont; 327 | } else { 328 | /* Regardless of whether or not a fallback font is found, the 329 | * character must be drawn. */ 330 | charexists = 1; 331 | 332 | fccharset = FcCharSetCreate(); 333 | FcCharSetAddChar(fccharset, utf8codepoint); 334 | 335 | if (!drw->fonts->pattern) { 336 | /* Refer to the comment in xfont_create for more information. */ 337 | die("the first font in the cache must be loaded from a font string."); 338 | } 339 | 340 | fcpattern = FcPatternDuplicate(drw->fonts->pattern); 341 | FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); 342 | FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); 343 | FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); 344 | 345 | FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); 346 | FcDefaultSubstitute(fcpattern); 347 | match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); 348 | 349 | FcCharSetDestroy(fccharset); 350 | FcPatternDestroy(fcpattern); 351 | 352 | if (match) { 353 | usedfont = xfont_create(drw, NULL, match); 354 | if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { 355 | for (curfont = drw->fonts; curfont->next; curfont = curfont->next) 356 | ; /* NOP */ 357 | curfont->next = usedfont; 358 | } else { 359 | xfont_free(usedfont); 360 | usedfont = drw->fonts; 361 | } 362 | } 363 | } 364 | } 365 | if (d) 366 | XftDrawDestroy(d); 367 | 368 | return x + (render ? w : 0); 369 | } 370 | 371 | void 372 | drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) 373 | { 374 | if (!drw) 375 | return; 376 | 377 | XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); 378 | XSync(drw->dpy, False); 379 | } 380 | 381 | unsigned int 382 | drw_fontset_getwidth(Drw *drw, const char *text) 383 | { 384 | if (!drw || !drw->fonts || !text) 385 | return 0; 386 | return drw_text(drw, 0, 0, 0, 0, 0, text, 0); 387 | } 388 | 389 | void 390 | drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) 391 | { 392 | XGlyphInfo ext; 393 | 394 | if (!font || !text) 395 | return; 396 | 397 | XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); 398 | if (w) 399 | *w = ext.xOff; 400 | if (h) 401 | *h = font->h; 402 | } 403 | 404 | Cur * 405 | drw_cur_create(Drw *drw, int shape) 406 | { 407 | Cur *cur; 408 | 409 | if (!drw || !(cur = (Cur*)ecalloc(1, sizeof(Cur)))) 410 | return NULL; 411 | 412 | cur->cursor = XCreateFontCursor(drw->dpy, shape); 413 | 414 | return cur; 415 | } 416 | 417 | void 418 | drw_cur_free(Drw *drw, Cur *cursor) 419 | { 420 | if (!cursor) 421 | return; 422 | 423 | XFreeCursor(drw->dpy, cursor->cursor); 424 | free(cursor); 425 | } 426 | -------------------------------------------------------------------------------- /drw.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | typedef struct { 4 | Cursor cursor; 5 | } Cur; 6 | 7 | typedef struct Fnt { 8 | Display *dpy; 9 | unsigned int h; 10 | XftFont *xfont; 11 | FcPattern *pattern; 12 | struct Fnt *next; 13 | } Fnt; 14 | 15 | enum { ColFg, ColBg, ColBorder }; /* Clr scheme index */ 16 | typedef XftColor Clr; 17 | 18 | typedef struct { 19 | unsigned int w, h; 20 | Display *dpy; 21 | int screen; 22 | Window root; 23 | Visual *visual; 24 | unsigned int depth; 25 | Colormap cmap; 26 | Drawable drawable; 27 | GC gc; 28 | Clr *scheme; 29 | Fnt *fonts; 30 | } Drw; 31 | 32 | /* Drawable abstraction */ 33 | Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap); 34 | void drw_resize(Drw *drw, unsigned int w, unsigned int h); 35 | void drw_free(Drw *drw); 36 | 37 | /* Fnt abstraction */ 38 | Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); 39 | void drw_fontset_free(Fnt* set); 40 | unsigned int drw_fontset_getwidth(Drw *drw, const char *text); 41 | void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); 42 | 43 | /* Colorscheme abstraction */ 44 | void drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha); 45 | Clr *drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount); 46 | 47 | /* Cursor abstraction */ 48 | Cur *drw_cur_create(Drw *drw, int shape); 49 | void drw_cur_free(Drw *drw, Cur *cursor); 50 | 51 | /* Drawing context manipulation */ 52 | void drw_setfontset(Drw *drw, Fnt *set); 53 | void drw_setscheme(Drw *drw, Clr *scm); 54 | 55 | /* Drawing functions */ 56 | void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); 57 | int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); 58 | 59 | /* Map functions */ 60 | void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); 61 | -------------------------------------------------------------------------------- /dwm.1: -------------------------------------------------------------------------------- 1 | .TH DWM 1 dwm\-VERSION 2 | .SH NAME 3 | dwm \- dynamic window manager 4 | .SH SYNOPSIS 5 | .B dwm 6 | .RB [ \-v ] 7 | .SH DESCRIPTION 8 | dwm is a dynamic window manager for X. It manages windows in tiled, monocle 9 | and floating layouts. Either layout can be applied dynamically, optimising the 10 | environment for the application in use and the task performed. 11 | .P 12 | In tiled layouts windows are managed in a master and stacking area. The master 13 | area on the left contains one window by default, and the stacking area on the 14 | right contains all other windows. The number of master area windows can be 15 | adjusted from zero to an arbitrary number. In monocle layout all windows are 16 | maximised to the screen size. In floating layout windows can be resized and 17 | moved freely. Dialog windows are always managed floating, regardless of the 18 | layout applied. 19 | .P 20 | Windows are grouped by tags. Each window can be tagged with one or multiple 21 | tags. Selecting certain tags displays all windows with these tags. 22 | .P 23 | Each screen contains a small status bar which displays all available tags, the 24 | layout, the title of the focused window, and the text read from the root window 25 | name property, if the screen is focused. A floating window is indicated with an 26 | empty square and a maximised floating window is indicated with a filled square 27 | before the windows title. The selected tags are indicated with a different 28 | color. The tags of the focused window are indicated with a filled square in the 29 | top left corner. The tags which are applied to one or more windows are 30 | indicated with an empty square in the top left corner. 31 | .P 32 | dwm draws a small border around windows to indicate the focus state. 33 | .SH OPTIONS 34 | .TP 35 | .B \-v 36 | prints version information to standard output, then exits. 37 | .SH USAGE 38 | .SS Status bar 39 | .TP 40 | .B X root window name 41 | is read and displayed in the status text area. It can be set with the 42 | .BR xsetroot (1) 43 | command. 44 | .TP 45 | .B Button1 46 | click on a tag label to display all windows with that tag, click on the layout 47 | label toggles between tiled and floating layout. 48 | .TP 49 | .B Button3 50 | click on a tag label adds/removes all windows with that tag to/from the view. 51 | .TP 52 | .B Mod1\-Button1 53 | click on a tag label applies that tag to the focused window. 54 | .TP 55 | .B Mod1\-Button3 56 | click on a tag label adds/removes that tag to/from the focused window. 57 | .SS Keyboard commands 58 | .TP 59 | .B Mod1\-Shift\-Return 60 | Start 61 | .BR st(1). 62 | .TP 63 | .B Mod1\-p 64 | Spawn 65 | .BR dmenu(1) 66 | for launching other programs. 67 | .TP 68 | .B Mod1\-, 69 | Focus previous screen, if any. 70 | .TP 71 | .B Mod1\-. 72 | Focus next screen, if any. 73 | .TP 74 | .B Mod1\-Shift\-, 75 | Send focused window to previous screen, if any. 76 | .TP 77 | .B Mod1\-Shift\-. 78 | Send focused window to next screen, if any. 79 | .TP 80 | .B Mod1\-Right 81 | Focus tag on the right, if any. 82 | .TP 83 | .B Mod1\-Left 84 | Focus tag on the left, if any. 85 | .TP 86 | .B Mod1\-Shift\-Right 87 | Send focused window to tag on the right, if any. 88 | .TP 89 | .B Mod1\-Shift\-Left 90 | Send focused window to tag on the left, if any. 91 | .TP 92 | .B Mod1\-b 93 | Toggles bar on and off. 94 | .TP 95 | .B Mod1\-t 96 | Sets tiled layout. 97 | .TP 98 | .B Mod1\-f 99 | Sets floating layout. 100 | .TP 101 | .B Mod1\-m 102 | Sets monocle layout. 103 | .TP 104 | .B Mod1\-space 105 | Toggles between current and previous layout. 106 | .TP 107 | .B Mod1\-j 108 | Focus next window. 109 | .TP 110 | .B Mod1\-k 111 | Focus previous window. 112 | .TP 113 | .B Mod1\-i 114 | Increase number of windows in master area. 115 | .TP 116 | .B Mod1\-d 117 | Decrease number of windows in master area. 118 | .TP 119 | .B Mod1\-l 120 | Increase master area size. 121 | .TP 122 | .B Mod1\-h 123 | Decrease master area size. 124 | .TP 125 | .B Mod1\-Return 126 | Zooms/cycles focused window to/from master area (tiled layouts only). 127 | .TP 128 | .B Mod1\-Shift\-c 129 | Close focused window. 130 | .TP 131 | .B Mod1\-Shift\-space 132 | Toggle focused window between tiled and floating state. 133 | .TP 134 | .B Mod1\-Tab 135 | Toggles to the previously selected tags. 136 | .TP 137 | .B Mod1\-Shift\-[1..n] 138 | Apply nth tag to focused window. 139 | .TP 140 | .B Mod1\-Shift\-0 141 | Apply all tags to focused window. 142 | .TP 143 | .B Mod1\-Control\-Shift\-[1..n] 144 | Add/remove nth tag to/from focused window. 145 | .TP 146 | .B Mod1\-[1..n] 147 | View all windows with nth tag. 148 | .TP 149 | .B Mod1\-0 150 | View all windows with any tag. 151 | .TP 152 | .B Mod1\-Control\-[1..n] 153 | Add/remove all windows with nth tag to/from the view. 154 | .TP 155 | .B Mod1\-Shift\-q 156 | Quit dwm. 157 | .TP 158 | .B Mod1\-Control\-Shift\-q 159 | Restart dwm. 160 | .SS Mouse commands 161 | .TP 162 | .B Mod1\-Button1 163 | Move focused window while dragging. Tiled windows will be toggled to the floating state. 164 | .TP 165 | .B Mod1\-Button2 166 | Toggles focused window between floating and tiled state. 167 | .TP 168 | .B Mod1\-Button3 169 | Resize focused window while dragging. Tiled windows will be toggled to the floating state. 170 | .SH CUSTOMIZATION 171 | dwm is customized by creating a custom config.h and (re)compiling the source 172 | code. This keeps it fast, secure and simple. 173 | .SH SIGNALS 174 | .TP 175 | .B SIGHUP - 1 176 | Restart the dwm process. 177 | .TP 178 | .B SIGTERM - 15 179 | Cleanly terminate the dwm process. 180 | .SH SEE ALSO 181 | .BR dmenu (1), 182 | .BR st (1) 183 | .SH ISSUES 184 | Java applications which use the XToolkit/XAWT backend may draw grey windows 185 | only. The XToolkit/XAWT backend breaks ICCCM-compliance in recent JDK 1.5 and early 186 | JDK 1.6 versions, because it assumes a reparenting window manager. Possible workarounds 187 | are using JDK 1.4 (which doesn't contain the XToolkit/XAWT backend) or setting the 188 | environment variable 189 | .BR AWT_TOOLKIT=MToolkit 190 | (to use the older Motif backend instead) or running 191 | .B xprop -root -f _NET_WM_NAME 32a -set _NET_WM_NAME LG3D 192 | or 193 | .B wmname LG3D 194 | (to pretend that a non-reparenting window manager is running that the 195 | XToolkit/XAWT backend can recognize) or when using OpenJDK setting the environment variable 196 | .BR _JAVA_AWT_WM_NONREPARENTING=1 . 197 | .SH BUGS 198 | Send all bug reports with a patch to hackers@suckless.org. 199 | -------------------------------------------------------------------------------- /dwm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gogongxt/dwm/43c5c05a6c79af49ffee910b8e1b7ac4aaa33e04/dwm.png -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "locked": { 5 | "lastModified": 1667395993, 6 | "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", 7 | "owner": "numtide", 8 | "repo": "flake-utils", 9 | "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "numtide", 14 | "repo": "flake-utils", 15 | "type": "github" 16 | } 17 | }, 18 | "nixpkgs": { 19 | "locked": { 20 | "lastModified": 1669387357, 21 | "narHash": "sha256-z1azVj/5Em5kGhh9OgBOsjTEgMab7hXL/aRilH9tzyI=", 22 | "owner": "NixOS", 23 | "repo": "nixpkgs", 24 | "rev": "55b3f68bda6d4f4dc6092eed0508063f154fa4fd", 25 | "type": "github" 26 | }, 27 | "original": { 28 | "id": "nixpkgs", 29 | "type": "indirect" 30 | } 31 | }, 32 | "root": { 33 | "inputs": { 34 | "flake-utils": "flake-utils", 35 | "nixpkgs": "nixpkgs" 36 | } 37 | } 38 | }, 39 | "root": "root", 40 | "version": 7 41 | } 42 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "A very basic flake"; 3 | 4 | inputs.flake-utils.url = "github:numtide/flake-utils"; 5 | 6 | outputs = { 7 | self, 8 | nixpkgs, 9 | flake-utils, 10 | }: 11 | flake-utils.lib.eachDefaultSystem ( 12 | system: let 13 | pkgs = nixpkgs.legacyPackages.${system}; 14 | in rec { 15 | packages = flake-utils.lib.flattenTree { 16 | dwm = pkgs.dwm.overrideAttrs (old: { 17 | src = pkgs.lib.cleanSource self; 18 | }); 19 | }; 20 | defaultPackage = packages.dwm; 21 | apps.default = flake-utils.lib.mkApp { 22 | drv = packages.dwm; 23 | exePath = "/bin/dwm"; 24 | }; 25 | } 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /flextile-deluxe.c: -------------------------------------------------------------------------------- 1 | typedef struct { 2 | void (*arrange)(Monitor *, int, int, int, int, int, int, int); 3 | } LayoutArranger; 4 | 5 | typedef struct { 6 | void (*arrange)(Monitor *, int, int, int, int, int, int, int, int, int); 7 | } TileArranger; 8 | 9 | static const LayoutArranger flexlayouts[] = { 10 | { layout_no_split }, 11 | { layout_split_vertical }, 12 | { layout_split_horizontal }, 13 | { layout_split_centered_vertical }, 14 | { layout_split_centered_horizontal }, 15 | { layout_split_vertical_dual_stack }, 16 | { layout_split_horizontal_dual_stack }, 17 | { layout_floating_master }, 18 | { layout_split_vertical_fixed }, 19 | { layout_split_horizontal_fixed }, 20 | { layout_split_centered_vertical_fixed }, 21 | { layout_split_centered_horizontal_fixed }, 22 | { layout_split_vertical_dual_stack_fixed }, 23 | { layout_split_horizontal_dual_stack_fixed }, 24 | { layout_floating_master_fixed }, 25 | }; 26 | 27 | static const TileArranger flextiles[] = { 28 | { arrange_top_to_bottom }, 29 | { arrange_left_to_right }, 30 | { arrange_monocle }, 31 | { arrange_gapplessgrid }, 32 | { arrange_gapplessgrid_alt1 }, 33 | { arrange_gapplessgrid_alt2 }, 34 | { arrange_gridmode }, 35 | { arrange_horizgrid }, 36 | { arrange_dwindle }, 37 | { arrange_spiral }, 38 | { arrange_tatami }, 39 | }; 40 | 41 | static void 42 | getfactsforrange(Monitor *m, int an, int ai, int size, int *rest, float *fact) 43 | { 44 | int i; 45 | float facts; 46 | Client *c; 47 | int total = 0; 48 | 49 | facts = 0; 50 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) 51 | if (i >= ai && i < (ai + an)) 52 | #if CFACTS_PATCH 53 | facts += c->cfact; 54 | #else 55 | facts += 1; 56 | #endif // CFACTS_PATCH 57 | 58 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) 59 | if (i >= ai && i < (ai + an)) 60 | #if CFACTS_PATCH 61 | total += size * (c->cfact / facts); 62 | #else 63 | total += size / facts; 64 | #endif // CFACTS_PATCH 65 | 66 | *rest = size - total; 67 | *fact = facts; 68 | } 69 | 70 | static void 71 | layout_no_split(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) 72 | { 73 | (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, n, 0); 74 | } 75 | 76 | static void 77 | layout_split_vertical(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) 78 | { 79 | /* Split master into master + stack if we have enough clients */ 80 | if (m->nmaster && n > m->nmaster) { 81 | layout_split_vertical_fixed(m, x, y, h, w, ih, iv, n); 82 | } else { 83 | layout_no_split(m, x, y, h, w, ih, iv, n); 84 | } 85 | } 86 | 87 | static void 88 | layout_split_vertical_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) 89 | { 90 | int sw, sx; 91 | 92 | sw = (w - iv) * (1 - m->mfact); 93 | w = (w - iv) * m->mfact; 94 | if (m->ltaxis[LAYOUT] < 0) { // mirror 95 | sx = x; 96 | x += sw + iv; 97 | } else { 98 | sx = x + w + iv; 99 | } 100 | 101 | (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0); 102 | (&flextiles[m->ltaxis[STACK]])->arrange(m, sx, y, h, sw, ih, iv, n, n - m->nmaster, m->nmaster); 103 | } 104 | 105 | static void 106 | layout_split_vertical_dual_stack(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) 107 | { 108 | /* Split master into master + stack if we have enough clients */ 109 | if (!m->nmaster || n <= m->nmaster) { 110 | layout_no_split(m, x, y, h, w, ih, iv, n); 111 | } else if (n <= m->nmaster + (m->nstack ? m->nstack : 1)) { 112 | layout_split_vertical(m, x, y, h, w, ih, iv, n); 113 | } else { 114 | layout_split_vertical_dual_stack_fixed(m, x, y, h, w, ih, iv, n); 115 | } 116 | } 117 | 118 | static void 119 | layout_split_vertical_dual_stack_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) 120 | { 121 | int sh, sw, sx, oy, sc; 122 | 123 | if (m->nstack) 124 | sc = m->nstack; 125 | else 126 | sc = (n - m->nmaster) / 2 + ((n - m->nmaster) % 2 > 0 ? 1 : 0); 127 | 128 | sw = (w - iv) * (1 - m->mfact); 129 | sh = (h - ih) / 2; 130 | w = (w - iv) * m->mfact; 131 | oy = y + sh + ih; 132 | if (m->ltaxis[LAYOUT] < 0) { // mirror 133 | sx = x; 134 | x += sw + iv; 135 | } else { 136 | sx = x + w + iv; 137 | } 138 | 139 | (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0); 140 | (&flextiles[m->ltaxis[STACK]])->arrange(m, sx, y, sh, sw, ih, iv, n, sc, m->nmaster); 141 | (&flextiles[m->ltaxis[STACK2]])->arrange(m, sx, oy, sh, sw, ih, iv, n, n - m->nmaster - sc, m->nmaster + sc); 142 | } 143 | 144 | static void 145 | layout_split_horizontal(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) 146 | { 147 | /* Split master into master + stack if we have enough clients */ 148 | if (m->nmaster && n > m->nmaster) { 149 | layout_split_horizontal_fixed(m, x, y, h, w, ih, iv, n); 150 | } else { 151 | layout_no_split(m, x, y, h, w, ih, iv, n); 152 | } 153 | } 154 | 155 | static void 156 | layout_split_horizontal_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) 157 | { 158 | int sh, sy; 159 | 160 | sh = (h - ih) * (1 - m->mfact); 161 | h = (h - ih) * m->mfact; 162 | if (m->ltaxis[LAYOUT] < 0) { // mirror 163 | sy = y; 164 | y += sh + ih; 165 | } else { 166 | sy = y + h + ih; 167 | } 168 | 169 | (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0); 170 | (&flextiles[m->ltaxis[STACK]])->arrange(m, x, sy, sh, w, ih, iv, n, n - m->nmaster, m->nmaster); 171 | } 172 | 173 | static void 174 | layout_split_horizontal_dual_stack(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) 175 | { 176 | /* Split master into master + stack if we have enough clients */ 177 | if (!m->nmaster || n <= m->nmaster) { 178 | layout_no_split(m, x, y, h, w, ih, iv, n); 179 | } else if (n <= m->nmaster + (m->nstack ? m->nstack : 1)) { 180 | layout_split_horizontal(m, x, y, h, w, ih, iv, n); 181 | } else { 182 | layout_split_horizontal_dual_stack_fixed(m, x, y, h, w, ih, iv, n); 183 | } 184 | } 185 | 186 | static void 187 | layout_split_horizontal_dual_stack_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) 188 | { 189 | int sh, sy, ox, sc; 190 | 191 | if (m->nstack) 192 | sc = m->nstack; 193 | else 194 | sc = (n - m->nmaster) / 2 + ((n - m->nmaster) % 2 > 0 ? 1 : 0); 195 | 196 | sh = (h - ih) * (1 - m->mfact); 197 | h = (h - ih) * m->mfact; 198 | sw = (w - iv) / 2; 199 | ox = x + sw + iv; 200 | if (m->ltaxis[LAYOUT] < 0) { // mirror 201 | sy = y; 202 | y += sh + ih; 203 | } else { 204 | sy = y + h + ih; 205 | } 206 | 207 | (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0); 208 | (&flextiles[m->ltaxis[STACK]])->arrange(m, x, sy, sh, sw, ih, iv, n, sc, m->nmaster); 209 | (&flextiles[m->ltaxis[STACK2]])->arrange(m, ox, sy, sh, sw, ih, iv, n, n - m->nmaster - sc, m->nmaster + sc); 210 | } 211 | 212 | static void 213 | layout_split_centered_vertical(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) 214 | { 215 | /* Split master into master + stack if we have enough clients */ 216 | if (!m->nmaster || n <= m->nmaster) { 217 | layout_no_split(m, x, y, h, w, ih, iv, n); 218 | } else if (n <= m->nmaster + (m->nstack ? m->nstack : 1)) { 219 | layout_split_vertical(m, x, y, h, w, ih, iv, n); 220 | } else { 221 | layout_split_centered_vertical_fixed(m, x, y, h, w, ih, iv, n); 222 | } 223 | } 224 | 225 | static void 226 | layout_split_centered_vertical_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) 227 | { 228 | int sw, sx, ox, sc; 229 | 230 | if (m->nstack) 231 | sc = m->nstack; 232 | else 233 | sc = (n - m->nmaster) / 2 + ((n - m->nmaster) % 2 > 0 ? 1 : 0); 234 | 235 | sw = (w - 2*iv) * (1 - m->mfact) / 2; 236 | w = (w - 2*iv) * m->mfact; 237 | if (m->ltaxis[LAYOUT] < 0) { // mirror 238 | sx = x; 239 | x += sw + iv; 240 | ox = x + w + iv; 241 | } else { 242 | ox = x; 243 | x += sw + iv; 244 | sx = x + w + iv; 245 | } 246 | 247 | (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0); 248 | (&flextiles[m->ltaxis[STACK]])->arrange(m, sx, y, h, sw, ih, iv, n, sc, m->nmaster); 249 | (&flextiles[m->ltaxis[STACK2]])->arrange(m, ox, y, h, sw, ih, iv, n, n - m->nmaster - sc, m->nmaster + sc); 250 | } 251 | 252 | static void 253 | layout_split_centered_horizontal(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) 254 | { 255 | /* Split master into master + stack if we have enough clients */ 256 | if (!m->nmaster || n <= m->nmaster) { 257 | layout_no_split(m, x, y, h, w, ih, iv, n); 258 | } else if (n <= m->nmaster + (m->nstack ? m->nstack : 1)) { 259 | layout_split_horizontal(m, x, y, h, w, ih, iv, n); 260 | } else { 261 | layout_split_centered_horizontal_fixed(m, x, y, h, w, ih, iv, n); 262 | } 263 | } 264 | 265 | static void 266 | layout_split_centered_horizontal_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) 267 | { 268 | int sh, sy, oy, sc; 269 | 270 | if (m->nstack) 271 | sc = m->nstack; 272 | else 273 | sc = (n - m->nmaster) / 2 + ((n - m->nmaster) % 2 > 0 ? 1 : 0); 274 | 275 | sh = (h - 2*ih) * (1 - m->mfact) / 2; 276 | h = (h - 2*ih) * m->mfact; 277 | if (m->ltaxis[LAYOUT] < 0) { // mirror 278 | sy = y; 279 | y += sh + ih; 280 | oy = y + h + ih; 281 | } else { 282 | oy = y; 283 | y += sh + ih; 284 | sy = y + h + ih; 285 | } 286 | 287 | (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0); 288 | (&flextiles[m->ltaxis[STACK]])->arrange(m, x, sy, sh, w, ih, iv, n, sc, m->nmaster); 289 | (&flextiles[m->ltaxis[STACK2]])->arrange(m, x, oy, sh, w, ih, iv, n, n - m->nmaster - sc, m->nmaster + sc); 290 | } 291 | 292 | static void 293 | layout_floating_master(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) 294 | { 295 | /* Split master into master + stack if we have enough clients */ 296 | if (!m->nmaster || n <= m->nmaster) { 297 | layout_no_split(m, x, y, h, w, ih, iv, n); 298 | } else { 299 | layout_floating_master_fixed(m, x, y, h, w, ih, iv, n); 300 | } 301 | } 302 | 303 | static void 304 | layout_floating_master_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) 305 | { 306 | int mh, mw; 307 | 308 | /* Draw stack area first */ 309 | (&flextiles[m->ltaxis[STACK]])->arrange(m, x, y, h, w, ih, iv, n, n - m->nmaster, m->nmaster); 310 | 311 | if (w > h) { 312 | mw = w * m->mfact; 313 | mh = h * 0.9; 314 | } else { 315 | mw = w * 0.9; 316 | mh = h * m->mfact; 317 | } 318 | x = x + (w - mw) / 2; 319 | y = y + (h - mh) / 2; 320 | 321 | (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, mh, mw, ih, iv, n, m->nmaster, 0); 322 | } 323 | 324 | static void 325 | arrange_left_to_right(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) 326 | { 327 | int i, rest; 328 | float facts, fact = 1; 329 | Client *c; 330 | 331 | if (ai + an > n) 332 | an = n - ai; 333 | 334 | w -= iv * (an - 1); 335 | getfactsforrange(m, an, ai, w, &rest, &facts); 336 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { 337 | if (i >= ai && i < (ai + an)) { 338 | #if CFACTS_PATCH 339 | fact = c->cfact; 340 | #endif // CFACTS_PATCH 341 | resize(c, x, y, w * (fact / facts) + ((i - ai) < rest ? 1 : 0) - (2*c->bw), h - (2*c->bw), 0); 342 | x += WIDTH(c) + iv; 343 | } 344 | } 345 | } 346 | 347 | static void 348 | arrange_top_to_bottom(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) 349 | { 350 | int i, rest; 351 | float facts, fact = 1; 352 | Client *c; 353 | 354 | if (ai + an > n) 355 | an = n - ai; 356 | 357 | h -= ih * (an - 1); 358 | getfactsforrange(m, an, ai, h, &rest, &facts); 359 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { 360 | if (i >= ai && i < (ai + an)) { 361 | #if CFACTS_PATCH 362 | fact = c->cfact; 363 | #endif // CFACTS_PATCH 364 | resize(c, x, y, w - (2*c->bw), h * (fact / facts) + ((i - ai) < rest ? 1 : 0) - (2*c->bw), 0); 365 | y += HEIGHT(c) + ih; 366 | } 367 | } 368 | } 369 | 370 | static void 371 | arrange_monocle(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) 372 | { 373 | int i; 374 | Client *c; 375 | 376 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) 377 | if (i >= ai && i < (ai + an)) 378 | resize(c, x, y, w - (2*c->bw), h - (2*c->bw), 0); 379 | } 380 | 381 | static void 382 | arrange_gridmode(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) 383 | { 384 | int i, cols, rows, ch, cw, cx, cy, cc, cr, chrest, cwrest; // counters 385 | Client *c; 386 | 387 | /* grid dimensions */ 388 | for (rows = 0; rows <= an/2; rows++) 389 | if (rows*rows >= an) 390 | break; 391 | cols = (rows && (rows - 1) * rows >= an) ? rows - 1 : rows; 392 | 393 | /* window geoms (cell height/width) */ 394 | ch = (h - ih * (rows - 1)) / (rows ? rows : 1); 395 | cw = (w - iv * (cols - 1)) / (cols ? cols : 1); 396 | chrest = h - ih * (rows - 1) - ch * rows; 397 | cwrest = w - iv * (cols - 1) - cw * cols; 398 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { 399 | if (i >= ai && i < (ai + an)) { 400 | cc = ((i - ai) / rows); // client column number 401 | cr = ((i - ai) % rows); // client row number 402 | cx = x + cc * (cw + iv) + MIN(cc, cwrest); 403 | cy = y + cr * (ch + ih) + MIN(cr, chrest); 404 | resize(c, cx, cy, cw + (cc < cwrest ? 1 : 0) - 2*c->bw, ch + (cr < chrest ? 1 : 0) - 2*c->bw, False); 405 | } 406 | } 407 | } 408 | 409 | static void 410 | arrange_horizgrid(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) 411 | { 412 | int ntop, nbottom, rh, rest; 413 | 414 | /* Exception when there is only one client; don't split into two rows */ 415 | if (an == 1) { 416 | arrange_monocle(m, x, y, h, w, ih, iv, n, an, ai); 417 | return; 418 | } 419 | 420 | ntop = an / 2; 421 | nbottom = an - ntop; 422 | rh = (h - ih) / 2; 423 | rest = h - ih - rh * 2; 424 | arrange_left_to_right(m, x, y, rh + rest, w, ih, iv, n, ntop, ai); 425 | arrange_left_to_right(m, x, y + rh + ih + rest, rh, w, ih, iv, n, nbottom, ai + ntop); 426 | } 427 | 428 | static void 429 | arrange_gapplessgrid(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) 430 | { 431 | int i, cols, rows, ch, cw, cn, rn, cc, rrest, crest; // counters 432 | Client *c; 433 | 434 | /* grid dimensions */ 435 | for (cols = 1; cols <= an/2; cols++) 436 | if (cols*cols >= an) 437 | break; 438 | if (an == 5) /* set layout against the general calculation: not 1:2:2, but 2:3 */ 439 | cols = 2; 440 | rows = an/cols; 441 | cn = rn = cc = 0; // reset column no, row no, client count 442 | 443 | ch = (h - ih * (rows - 1)) / rows; 444 | rrest = (h - ih * (rows - 1)) - ch * rows; 445 | cw = (w - iv * (cols - 1)) / cols; 446 | crest = (w - iv * (cols - 1)) - cw * cols; 447 | 448 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { 449 | if (i >= ai && i < (ai + an)) { 450 | if (cc/rows + 1 > cols - an%cols) { 451 | rows = an/cols + 1; 452 | ch = (h - ih * (rows - 1)) / rows; 453 | rrest = (h - ih * (rows - 1)) - ch * rows; 454 | } 455 | resize(c, 456 | x, 457 | y + rn*(ch + ih) + MIN(rn, rrest), 458 | cw + (cn < crest ? 1 : 0) - 2*c->bw, 459 | ch + (rn < rrest ? 1 : 0) - 2*c->bw, 460 | 0); 461 | rn++; 462 | cc++; 463 | if (rn >= rows) { 464 | rn = 0; 465 | x += cw + ih + (cn < crest ? 1 : 0); 466 | cn++; 467 | } 468 | } 469 | } 470 | } 471 | 472 | /* This version of gappless grid fills rows first */ 473 | static void 474 | arrange_gapplessgrid_alt1(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) 475 | { 476 | int i, cols, rows, rest, ch; 477 | 478 | /* grid dimensions */ 479 | for (cols = 1; cols <= an/2; cols++) 480 | if (cols*cols >= an) 481 | break; 482 | rows = (cols && (cols - 1) * cols >= an) ? cols - 1 : cols; 483 | ch = (h - ih * (rows - 1)) / (rows ? rows : 1); 484 | rest = (h - ih * (rows - 1)) - ch * rows; 485 | 486 | for (i = 0; i < rows; i++) { 487 | arrange_left_to_right(m, x, y, ch + (i < rest ? 1 : 0), w, ih, iv, n, MIN(cols, an - i*cols), ai + i*cols); 488 | y += ch + (i < rest ? 1 : 0) + ih; 489 | } 490 | } 491 | 492 | /* This version of gappless grid fills columns first */ 493 | static void 494 | arrange_gapplessgrid_alt2(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) 495 | { 496 | int i, cols, rows, rest, cw; 497 | 498 | /* grid dimensions */ 499 | for (rows = 0; rows <= an/2; rows++) 500 | if (rows*rows >= an) 501 | break; 502 | cols = (rows && (rows - 1) * rows >= an) ? rows - 1 : rows; 503 | cw = (w - iv * (cols - 1)) / (cols ? cols : 1); 504 | rest = (w - iv * (cols - 1)) - cw * cols; 505 | 506 | for (i = 0; i < cols; i++) { 507 | arrange_top_to_bottom(m, x, y, h, cw + (i < rest ? 1 : 0), ih, iv, n, MIN(rows, an - i*rows), ai + i*rows); 508 | x += cw + (i < rest ? 1 : 0) + iv; 509 | } 510 | } 511 | 512 | static void 513 | arrange_fibonacci(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai, int s) 514 | { 515 | int i, j, nv, hrest = 0, wrest = 0, nx = x, ny = y, nw = w, nh = h, r = 1; 516 | Client *c; 517 | 518 | for (i = 0, j = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), j++) { 519 | if (j >= ai && j < (ai + an)) { 520 | if (r) { 521 | if ((i % 2 && ((nh - ih) / 2) <= (bh + 2*c->bw)) || (!(i % 2) && ((nw - iv) / 2) <= (bh + 2*c->bw))) { 522 | r = 0; 523 | } 524 | if (r && i < an - 1) { 525 | if (i % 2) { 526 | nv = (nh - ih) / 2; 527 | hrest = nh - 2*nv - ih; 528 | nh = nv; 529 | } else { 530 | nv = (nw - iv) / 2; 531 | wrest = nw - 2*nv - iv; 532 | nw = nv; 533 | } 534 | 535 | if ((i % 4) == 2 && !s) 536 | nx += nw + iv; 537 | else if ((i % 4) == 3 && !s) 538 | ny += nh + ih; 539 | } 540 | if ((i % 4) == 0) { 541 | if (s) { 542 | ny += nh + ih; 543 | nh += hrest; 544 | } else { 545 | nh -= hrest; 546 | ny -= nh + ih; 547 | } 548 | } else if ((i % 4) == 1) { 549 | nx += nw + iv; 550 | nw += wrest; 551 | } else if ((i % 4) == 2) { 552 | ny += nh + ih; 553 | nh += hrest; 554 | if (i < n - 1) 555 | nw += wrest; 556 | } else if ((i % 4) == 3) { 557 | if (s) { 558 | nx += nw + iv; 559 | nw -= wrest; 560 | } else { 561 | nw -= wrest; 562 | nx -= nw + iv; 563 | nh += hrest; 564 | } 565 | } 566 | if (i == 0) { 567 | if (an != 1) { 568 | nw = (w - iv) - (w - iv) * (1 - m->mfact); 569 | wrest = 0; 570 | } 571 | ny = y; 572 | } else if (i == 1) 573 | nw = w - nw - iv; 574 | i++; 575 | } 576 | 577 | resize(c, nx, ny, nw - 2 * c->bw, nh - 2*c->bw, False); 578 | } 579 | } 580 | } 581 | 582 | static void 583 | arrange_dwindle(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) 584 | { 585 | arrange_fibonacci(m, x, y, h, w, ih, iv, n, an, ai, 1); 586 | } 587 | 588 | static void 589 | arrange_spiral(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) 590 | { 591 | arrange_fibonacci(m, x, y, h, w, ih, iv, n, an, ai, 0); 592 | } 593 | 594 | static void 595 | arrange_tatami(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) 596 | { 597 | unsigned int i, j, nx, ny, nw, nh, tnx, tny, tnw, tnh, nhrest, hrest, wrest, areas, mats, cats; 598 | Client *c; 599 | 600 | nx = x; 601 | ny = y; 602 | nw = w; 603 | nh = h; 604 | 605 | mats = an / 5; 606 | cats = an % 5; 607 | hrest = 0; 608 | wrest = 0; 609 | 610 | areas = mats + (cats > 0); 611 | nh = (h - ih * (areas - 1)) / areas; 612 | nhrest = (h - ih * (areas - 1)) % areas; 613 | 614 | for (i = 0, j = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), j++) { 615 | if (j >= ai && j < (ai + an)) { 616 | 617 | tnw = nw; 618 | tnx = nx; 619 | tnh = nh; 620 | tny = ny; 621 | 622 | if (j < ai + cats) { 623 | /* Arrange cats (all excess clients that can't be tiled as mats). Cats sleep on mats. */ 624 | 625 | switch (cats) { 626 | case 1: // fill 627 | break; 628 | case 2: // up and down 629 | if ((i % 5) == 0) //up 630 | tnh = (nh - ih) / 2 + (nh - ih) % 2; 631 | else if ((i % 5) == 1) { //down 632 | tny += (nh - ih) / 2 + (nh - ih) % 2 + ih; 633 | tnh = (nh - ih) / 2; 634 | } 635 | break; 636 | case 3: //bottom, up-left and up-right 637 | if ((i % 5) == 0) { // up-left 638 | tnw = (nw - iv) / 2 + (nw - iv) % 2; 639 | tnh = (nh - ih) * 2 / 3 + (nh - ih) * 2 % 3; 640 | } else if ((i % 5) == 1) { // up-right 641 | tnx += (nw - iv) / 2 + (nw - iv) % 2 + iv; 642 | tnw = (nw - iv) / 2; 643 | tnh = (nh - ih) * 2 / 3 + (nh - ih) * 2 % 3; 644 | } else if ((i % 5) == 2) { //bottom 645 | tnh = (nh - ih) / 3; 646 | tny += (nh - ih) * 2 / 3 + (nh - ih) * 2 % 3 + ih; 647 | } 648 | break; 649 | case 4: // bottom, left, right and top 650 | if ((i % 5) == 0) { //top 651 | hrest = (nh - 2 * ih) % 4; 652 | tnh = (nh - 2 * ih) / 4 + (hrest ? 1 : 0); 653 | } else if ((i % 5) == 1) { // left 654 | tnw = (nw - iv) / 2 + (nw - iv) % 2; 655 | tny += (nh - 2 * ih) / 4 + (hrest ? 1 : 0) + ih; 656 | tnh = (nh - 2 * ih) * 2 / 4 + (hrest > 1 ? 1 : 0); 657 | } else if ((i % 5) == 2) { // right 658 | tnx += (nw - iv) / 2 + (nw - iv) % 2 + iv; 659 | tnw = (nw - iv) / 2; 660 | tny += (nh - 2 * ih) / 4 + (hrest ? 1 : 0) + ih; 661 | tnh = (nh - 2 * ih) * 2 / 4 + (hrest > 1 ? 1 : 0); 662 | } else if ((i % 5) == 3) { // bottom 663 | tny += (nh - 2 * ih) / 4 + (hrest ? 1 : 0) + (nh - 2 * ih) * 2 / 4 + (hrest > 1 ? 1 : 0) + 2 * ih; 664 | tnh = (nh - 2 * ih) / 4 + (hrest > 2 ? 1 : 0); 665 | } 666 | break; 667 | } 668 | 669 | } else { 670 | /* Arrange mats. One mat is a collection of five clients arranged tatami style */ 671 | 672 | if (((i - cats) % 5) == 0) { 673 | if ((cats > 0) || ((i - cats) >= 5)) { 674 | tny = ny = ny + nh + (nhrest > 0 ? 1 : 0) + ih; 675 | --nhrest; 676 | } 677 | } 678 | 679 | switch ((i - cats) % 5) { 680 | case 0: // top-left-vert 681 | wrest = (nw - 2 * iv) % 3; 682 | hrest = (nh - 2 * ih) % 3; 683 | tnw = (nw - 2 * iv) / 3 + (wrest ? 1 : 0); 684 | tnh = (nh - 2 * ih) * 2 / 3 + hrest + iv; 685 | break; 686 | case 1: // top-right-hor 687 | tnx += (nw - 2 * iv) / 3 + (wrest ? 1 : 0) + iv; 688 | tnw = (nw - 2 * iv) * 2 / 3 + (wrest > 1 ? 1 : 0) + iv; 689 | tnh = (nh - 2 * ih) / 3 + (hrest ? 1 : 0); 690 | break; 691 | case 2: // center 692 | tnx += (nw - 2 * iv) / 3 + (wrest ? 1 : 0) + iv; 693 | tnw = (nw - 2 * iv) / 3 + (wrest > 1 ? 1 : 0); 694 | tny += (nh - 2 * ih) / 3 + (hrest ? 1 : 0) + ih; 695 | tnh = (nh - 2 * ih) / 3 + (hrest > 1 ? 1 : 0); 696 | break; 697 | case 3: // bottom-right-vert 698 | tnx += (nw - 2 * iv) * 2 / 3 + wrest + 2 * iv; 699 | tnw = (nw - 2 * iv) / 3; 700 | tny += (nh - 2 * ih) / 3 + (hrest ? 1 : 0) + ih; 701 | tnh = (nh - 2 * ih) * 2 / 3 + hrest + iv; 702 | break; 703 | case 4: // (oldest) bottom-left-hor 704 | tnw = (nw - 2 * iv) * 2 / 3 + wrest + iv; 705 | tny += (nh - 2 * ih) * 2 / 3 + hrest + 2 * iv; 706 | tnh = (nh - 2 * ih) / 3; 707 | break; 708 | } 709 | 710 | } 711 | 712 | resize(c, tnx, tny, tnw - 2 * c->bw, tnh - 2 * c->bw, False); 713 | ++i; 714 | } 715 | } 716 | } 717 | 718 | static void 719 | flextile(Monitor *m) 720 | { 721 | unsigned int n; 722 | int oh = 0, ov = 0, ih = 0, iv = 0; // gaps outer/inner horizontal/vertical 723 | 724 | #if VANITYGAPS_PATCH 725 | getgaps(m, &oh, &ov, &ih, &iv, &n); 726 | #else 727 | Client *c; 728 | for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); 729 | #endif // VANITYGAPS_PATCH 730 | 731 | if (m->lt[m->sellt]->preset.layout != m->ltaxis[LAYOUT] || 732 | m->lt[m->sellt]->preset.masteraxis != m->ltaxis[MASTER] || 733 | m->lt[m->sellt]->preset.stack1axis != m->ltaxis[STACK] || 734 | m->lt[m->sellt]->preset.stack2axis != m->ltaxis[STACK2]) 735 | setflexsymbols(m, n); 736 | else if (m->lt[m->sellt]->preset.symbolfunc != NULL) 737 | m->lt[m->sellt]->preset.symbolfunc(m, n); 738 | 739 | if (n == 0) 740 | return; 741 | 742 | #if VANITYGAPS_PATCH && !VANITYGAPS_MONOCLE_PATCH 743 | /* No outer gap if full screen monocle */ 744 | if (abs(m->ltaxis[MASTER]) == MONOCLE && (abs(m->ltaxis[LAYOUT]) == NO_SPLIT || n <= m->nmaster)) { 745 | oh = 0; 746 | ov = 0; 747 | } 748 | #endif // VANITYGAPS_PATCH && !VANITYGAPS_MONOCLE_PATCH 749 | 750 | // gxt_kt set the gap 751 | ov=gappo; 752 | oh=gappo; 753 | ih=gappi; 754 | iv=gappi; 755 | for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); 756 | 757 | (&flexlayouts[abs(m->ltaxis[LAYOUT])])->arrange(m, m->wx + ov, m->wy + oh, m->wh - 2*oh, m->ww - 2*ov, ih, iv, n); 758 | return; 759 | } 760 | 761 | static void 762 | setflexsymbols(Monitor *m, unsigned int n) 763 | { 764 | int l; 765 | char sym1, sym2, sym3; 766 | Client *c; 767 | 768 | if (n == 0) 769 | for (c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); 770 | 771 | l = abs(m->ltaxis[LAYOUT]); 772 | if (m->ltaxis[MASTER] == MONOCLE && (l == NO_SPLIT || !m->nmaster || n <= m->nmaster)) { 773 | monoclesymbols(m, n); 774 | return; 775 | } 776 | 777 | if (m->ltaxis[STACK] == MONOCLE && (l == SPLIT_VERTICAL || l == SPLIT_HORIZONTAL_FIXED)) { 778 | decksymbols(m, n); 779 | return; 780 | } 781 | 782 | /* Layout symbols */ 783 | if (l == NO_SPLIT || !m->nmaster) { 784 | sym1 = sym2 = sym3 = (int)tilesymb[m->ltaxis[MASTER]]; 785 | } else { 786 | sym2 = layoutsymb[l]; 787 | if (m->ltaxis[LAYOUT] < 0) { 788 | sym1 = tilesymb[m->ltaxis[STACK]]; 789 | sym3 = tilesymb[m->ltaxis[MASTER]]; 790 | } else { 791 | sym1 = tilesymb[m->ltaxis[MASTER]]; 792 | sym3 = tilesymb[m->ltaxis[STACK]]; 793 | } 794 | } 795 | 796 | snprintf(m->ltsymbol, sizeof m->ltsymbol, "%c%c%c", sym1, sym2, sym3); 797 | } 798 | 799 | static void 800 | monoclesymbols(Monitor *m, unsigned int n) 801 | { 802 | if (n > 0) 803 | snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); 804 | else 805 | snprintf(m->ltsymbol, sizeof m->ltsymbol, "[M]"); 806 | } 807 | 808 | static void 809 | decksymbols(Monitor *m, unsigned int n) 810 | { 811 | if (n > m->nmaster) 812 | snprintf(m->ltsymbol, sizeof m->ltsymbol, "[]%d", n); 813 | else 814 | snprintf(m->ltsymbol, sizeof m->ltsymbol, "[D]"); 815 | } 816 | 817 | /* Mirror layout axis for flextile */ 818 | void 819 | mirrorlayout(const Arg *arg) 820 | { 821 | if (!selmon->lt[selmon->sellt]->arrange) 822 | return; 823 | selmon->ltaxis[LAYOUT] *= -1; 824 | selmon->pertag->ltaxis[selmon->pertag->curtag][0] = selmon->ltaxis[LAYOUT]; 825 | arrange(selmon); 826 | } 827 | 828 | /* Rotate layout axis for flextile */ 829 | void 830 | rotatelayoutaxis(const Arg *arg) 831 | { 832 | int incr = (arg->i > 0 ? 1 : -1); 833 | int axis = abs(arg->i) - 1; 834 | 835 | if (!selmon->lt[selmon->sellt]->arrange) 836 | return; 837 | if (axis == LAYOUT) { 838 | if (selmon->ltaxis[LAYOUT] >= 0) { 839 | selmon->ltaxis[LAYOUT] += incr; 840 | if (selmon->ltaxis[LAYOUT] >= LAYOUT_LAST) 841 | selmon->ltaxis[LAYOUT] = 0; 842 | else if (selmon->ltaxis[LAYOUT] < 0) 843 | selmon->ltaxis[LAYOUT] = LAYOUT_LAST - 1; 844 | } else { 845 | selmon->ltaxis[LAYOUT] -= incr; 846 | if (selmon->ltaxis[LAYOUT] <= -LAYOUT_LAST) 847 | selmon->ltaxis[LAYOUT] = 0; 848 | else if (selmon->ltaxis[LAYOUT] > 0) 849 | selmon->ltaxis[LAYOUT] = -LAYOUT_LAST + 1; 850 | } 851 | } else { 852 | selmon->ltaxis[axis] += incr; 853 | if (selmon->ltaxis[axis] >= AXIS_LAST) 854 | selmon->ltaxis[axis] = 0; 855 | else if (selmon->ltaxis[axis] < 0) 856 | selmon->ltaxis[axis] = AXIS_LAST - 1; 857 | } 858 | selmon->pertag->ltaxis[selmon->pertag->curtag][axis] = selmon->ltaxis[axis]; 859 | arrange(selmon); 860 | setflexsymbols(selmon, 0); 861 | } 862 | 863 | void 864 | incnstack(const Arg *arg) 865 | { 866 | selmon->nstack = selmon->pertag->nstacks[selmon->pertag->curtag] = MAX(selmon->nstack + arg->i, 0); 867 | arrange(selmon); 868 | } 869 | -------------------------------------------------------------------------------- /flextile-deluxe.h: -------------------------------------------------------------------------------- 1 | static void flextile(Monitor *m); 2 | static void mirrorlayout(const Arg *arg); 3 | static void rotatelayoutaxis(const Arg *arg); 4 | static void incnstack(const Arg *arg); 5 | 6 | /* Symbol handlers */ 7 | static void setflexsymbols(Monitor *m, unsigned int n); 8 | static void monoclesymbols(Monitor *m, unsigned int n); 9 | static void decksymbols(Monitor *m, unsigned int n); 10 | 11 | /* Layout split */ 12 | static void layout_no_split(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); 13 | static void layout_split_vertical(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); 14 | static void layout_split_horizontal(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); 15 | static void layout_split_vertical_dual_stack(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); 16 | static void layout_split_horizontal_dual_stack(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); 17 | static void layout_split_centered_vertical(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); 18 | static void layout_split_centered_horizontal(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); 19 | static void layout_floating_master(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); 20 | static void layout_split_vertical_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); 21 | static void layout_split_horizontal_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); 22 | static void layout_split_vertical_dual_stack_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); 23 | static void layout_split_horizontal_dual_stack_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); 24 | static void layout_split_centered_vertical_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); 25 | static void layout_split_centered_horizontal_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); 26 | static void layout_floating_master_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); 27 | 28 | /* Layout tile arrangements */ 29 | static void arrange_left_to_right(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); 30 | static void arrange_top_to_bottom(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); 31 | static void arrange_monocle(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); 32 | static void arrange_gapplessgrid(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); 33 | static void arrange_gapplessgrid_alt1(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); 34 | static void arrange_gapplessgrid_alt2(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); 35 | static void arrange_gridmode(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); 36 | static void arrange_horizgrid(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); 37 | static void arrange_dwindle(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); 38 | static void arrange_spiral(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); 39 | static void arrange_tatami(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); 40 | 41 | /* Named flextile constants */ 42 | enum { 43 | LAYOUT, // controls overall layout arrangement / split 44 | MASTER, // indicates the tile arrangement for the master area 45 | STACK, // indicates the tile arrangement for the stack area 46 | STACK2, // indicates the tile arrangement for the secondary stack area 47 | LTAXIS_LAST, 48 | }; 49 | 50 | /* Layout arrangements */ 51 | enum { 52 | NO_SPLIT, 53 | SPLIT_VERTICAL, // master stack vertical split 54 | SPLIT_HORIZONTAL, // master stack horizontal split 55 | SPLIT_CENTERED_VERTICAL, // centered master vertical split 56 | SPLIT_CENTERED_HORIZONTAL, // centered master horizontal split 57 | SPLIT_VERTICAL_DUAL_STACK, // master stack vertical split with dual stack 58 | SPLIT_HORIZONTAL_DUAL_STACK, // master stack vertical split with dual stack 59 | FLOATING_MASTER, // (fake) floating master 60 | SPLIT_VERTICAL_FIXED, // master stack vertical fixed split 61 | SPLIT_HORIZONTAL_FIXED, // master stack horizontal fixed split 62 | SPLIT_CENTERED_VERTICAL_FIXED, // centered master vertical fixed split 63 | SPLIT_CENTERED_HORIZONTAL_FIXED, // centered master horizontal fixed split 64 | SPLIT_VERTICAL_DUAL_STACK_FIXED, // master stack vertical split with fixed dual stack 65 | SPLIT_HORIZONTAL_DUAL_STACK_FIXED, // master stack vertical split with fixed dual stack 66 | FLOATING_MASTER_FIXED, // (fake) fixed floating master 67 | LAYOUT_LAST, 68 | }; 69 | 70 | static char layoutsymb[] = { 71 | 32, // " ", 72 | 124, // "|", 73 | 61, // "=", 74 | 94, // "^", 75 | 126, // "~", 76 | 58, // ":", 77 | 59, // ";", 78 | 43, // "+", 79 | 124, // "¦", 80 | 61, // "=", 81 | 94, // "^", 82 | 126, // "~", 83 | 58, // ":", 84 | 59, // ";", 85 | 43, // "+", 86 | }; 87 | 88 | /* Tile arrangements */ 89 | enum { 90 | TOP_TO_BOTTOM, // clients are arranged vertically 91 | LEFT_TO_RIGHT, // clients are arranged horizontally 92 | MONOCLE, // clients are arranged in deck / monocle mode 93 | GAPPLESSGRID, // clients are arranged in a gappless grid (original formula) 94 | GAPPLESSGRID_ALT1, // clients are arranged in a gappless grid (alt. 1, fills rows first) 95 | GAPPLESSGRID_ALT2, // clients are arranged in a gappless grid (alt. 2, fills columns first) 96 | GRIDMODE, // clients are arranged in a grid 97 | HORIZGRID, // clients are arranged in a horizontal grid 98 | DWINDLE, // clients are arranged in fibonacci dwindle mode 99 | SPIRAL, // clients are arranged in fibonacci spiral mode 100 | TATAMI, // clients are arranged as tatami mats 101 | AXIS_LAST, 102 | }; 103 | 104 | static char tilesymb[] = { 105 | 61, // "=", 106 | 124, // "|", 107 | 68, // "D", 108 | 71, // "G", 109 | 49, // "1", 110 | 50, // "2" 111 | 35, // "#", 112 | 126, // "~", 113 | 92, // "\\", 114 | 64, // "@", 115 | 84, // "T", 116 | }; 117 | -------------------------------------------------------------------------------- /fonts/JoyPixels.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gogongxt/dwm/43c5c05a6c79af49ffee910b8e1b7ac4aaa33e04/fonts/JoyPixels.ttf -------------------------------------------------------------------------------- /fonts/Monaco Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gogongxt/dwm/43c5c05a6c79af49ffee910b8e1b7ac4aaa33e04/fonts/Monaco Regular.ttf -------------------------------------------------------------------------------- /fonts/SymbolsNerdFont-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gogongxt/dwm/43c5c05a6c79af49ffee910b8e1b7ac4aaa33e04/fonts/SymbolsNerdFont-Regular.ttf -------------------------------------------------------------------------------- /fonts/chinese.msyh.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gogongxt/dwm/43c5c05a6c79af49ffee910b8e1b7ac4aaa33e04/fonts/chinese.msyh.ttf -------------------------------------------------------------------------------- /i3lock/lock.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BLANK='#00000000' 4 | CLEAR='#ffffff22' 5 | DEFAULT='#cdb0eecc' 6 | TEXT='#f48484ee' 7 | WRONG='#880000bb' 8 | VERIFYING='#bb00bbbb' 9 | 10 | i3lock \ 11 | --insidever-color=$CLEAR \ 12 | --ringver-color=$VERIFYING \ 13 | \ 14 | --insidewrong-color=$CLEAR \ 15 | --ringwrong-color=$WRONG \ 16 | \ 17 | --inside-color=$BLANK \ 18 | --ring-color=$DEFAULT \ 19 | --line-color=$BLANK \ 20 | --separator-color=$DEFAULT \ 21 | \ 22 | --verif-color=$TEXT \ 23 | --wrong-color=#ff0000 \ 24 | --time-color=$TEXT \ 25 | --date-color=#a5c689 \ 26 | --layout-color=$TEXT \ 27 | --keyhl-color=$WRONG \ 28 | --bshl-color=$WRONG \ 29 | \ 30 | --screen 1 \ 31 | --blur 3 \ 32 | --clock \ 33 | --force-clock \ 34 | --indicator \ 35 | --time-str="%H:%M:%S" \ 36 | --time-size=128 \ 37 | --date-str="%A %Y-%m-%d" \ 38 | --date-size=30 \ 39 | --wrong-size=60 \ 40 | --verif-size=60 \ 41 | --radius=300 \ 42 | --pointer default \ 43 | --show-failed-attempts 44 | # --keylayout 1 \ 45 | # --time-font="Monaco" \ 46 | 47 | xdotool mousemove_relative 1 1 # 该命令用于解决自动锁屏后未展示锁屏界面的问题(移动一下鼠标) 48 | -------------------------------------------------------------------------------- /patch/dwm-autostart-20161205-bb3bd6f.diff: -------------------------------------------------------------------------------- 1 | commit 5918623c5bd7fda155bf9dc3d33890c4ae1722d0 2 | Author: Simon Bremer 3 | Date: Thu Dec 22 17:31:07 2016 +0100 4 | 5 | Applied and fixed autostart patch for previous version; 6 | 7 | diff --git a/dwm.c b/dwm.c 8 | index d27cb67..066ed71 100644 9 | --- a/dwm.c 10 | +++ b/dwm.c 11 | @@ -194,6 +194,7 @@ static void resizeclient(Client *c, int x, int y, int w, int h); 12 | static void resizemouse(const Arg *arg); 13 | static void restack(Monitor *m); 14 | static void run(void); 15 | +static void runAutostart(void); 16 | static void scan(void); 17 | static int sendevent(Client *c, Atom proto); 18 | static void sendmon(Client *c, Monitor *m); 19 | @@ -1386,6 +1387,12 @@ run(void) 20 | } 21 | 22 | void 23 | +runAutostart(void) { 24 | + system("cd ~/.dwm; ./autostart_blocking.sh"); 25 | + system("cd ~/.dwm; ./autostart.sh &"); 26 | +} 27 | + 28 | +void 29 | scan(void) 30 | { 31 | unsigned int i, num; 32 | @@ -2145,6 +2152,7 @@ main(int argc, char *argv[]) 33 | checkotherwm(); 34 | setup(); 35 | scan(); 36 | + runAutostart(); 37 | run(); 38 | cleanup(); 39 | XCloseDisplay(dpy); 40 | -------------------------------------------------------------------------------- /patch/dwm-cyclelayouts-20180524-6.2.diff: -------------------------------------------------------------------------------- 1 | From a09e766a4342f580582082a92b2de65f33208eb4 Mon Sep 17 00:00:00 2001 2 | From: Christopher Drelich 3 | Date: Thu, 24 May 2018 00:56:56 -0400 4 | Subject: [PATCH] Function to cycle through available layouts. 5 | 6 | MOD-CTRL-, and MOD-CTRL-. 7 | cycle backwards and forwards through available layouts. 8 | Probably only useful if you have a lot of additional layouts. 9 | The NULL, NULL layout should always be the last layout in your list, 10 | in order to guarantee consistent behavior. 11 | --- 12 | config.def.h | 3 +++ 13 | dwm.1 | 6 ++++++ 14 | dwm.c | 18 ++++++++++++++++++ 15 | 3 files changed, 27 insertions(+) 16 | 17 | diff --git a/config.def.h b/config.def.h 18 | index a9ac303..153b880 100644 19 | --- a/config.def.h 20 | +++ b/config.def.h 21 | @@ -41,6 +41,7 @@ static const Layout layouts[] = { 22 | { "[]=", tile }, /* first entry is default */ 23 | { "><>", NULL }, /* no layout function means floating behavior */ 24 | { "[M]", monocle }, 25 | + { NULL, NULL }, 26 | }; 27 | 28 | /* key definitions */ 29 | @@ -76,6 +77,8 @@ static Key keys[] = { 30 | { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, 31 | { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, 32 | { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, 33 | + { MODKEY|ControlMask, XK_comma, cyclelayout, {.i = -1 } }, 34 | + { MODKEY|ControlMask, XK_period, cyclelayout, {.i = +1 } }, 35 | { MODKEY, XK_space, setlayout, {0} }, 36 | { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, 37 | { MODKEY, XK_0, view, {.ui = ~0 } }, 38 | diff --git a/dwm.1 b/dwm.1 39 | index 13b3729..165891b 100644 40 | --- a/dwm.1 41 | +++ b/dwm.1 42 | @@ -92,6 +92,12 @@ Sets monocle layout. 43 | .B Mod1\-space 44 | Toggles between current and previous layout. 45 | .TP 46 | +.B Mod1\-Control\-, 47 | +Cycles backwards in layout list. 48 | +.TP 49 | +.B Mod1\-Control\-. 50 | +Cycles forwards in layout list. 51 | +.TP 52 | .B Mod1\-j 53 | Focus next window. 54 | .TP 55 | diff --git a/dwm.c b/dwm.c 56 | index bb95e26..db73000 100644 57 | --- a/dwm.c 58 | +++ b/dwm.c 59 | @@ -157,6 +157,7 @@ static void configure(Client *c); 60 | static void configurenotify(XEvent *e); 61 | static void configurerequest(XEvent *e); 62 | static Monitor *createmon(void); 63 | +static void cyclelayout(const Arg *arg); 64 | static void destroynotify(XEvent *e); 65 | static void detach(Client *c); 66 | static void detachstack(Client *c); 67 | @@ -645,6 +646,23 @@ createmon(void) 68 | } 69 | 70 | void 71 | +cyclelayout(const Arg *arg) { 72 | + Layout *l; 73 | + for(l = (Layout *)layouts; l != selmon->lt[selmon->sellt]; l++); 74 | + if(arg->i > 0) { 75 | + if(l->symbol && (l + 1)->symbol) 76 | + setlayout(&((Arg) { .v = (l + 1) })); 77 | + else 78 | + setlayout(&((Arg) { .v = layouts })); 79 | + } else { 80 | + if(l != layouts && (l - 1)->symbol) 81 | + setlayout(&((Arg) { .v = (l - 1) })); 82 | + else 83 | + setlayout(&((Arg) { .v = &layouts[LENGTH(layouts) - 2] })); 84 | + } 85 | +} 86 | + 87 | +void 88 | destroynotify(XEvent *e) 89 | { 90 | Client *c; 91 | -- 92 | 2.7.4 93 | -------------------------------------------------------------------------------- /patch/dwm-doublepressquit-6.3.diff: -------------------------------------------------------------------------------- 1 | From 1ff5d32e691fa8fb001d0619b72da11ef8232009 Mon Sep 17 00:00:00 2001 2 | From: Sebastian Karlsen 3 | Date: Tue, 1 Mar 2022 14:49:03 +0100 4 | Subject: [PATCH] Only quit dwm if binding is pressed twice quickly 5 | 6 | --- 7 | config.def.h | 3 +++ 8 | dwm.c | 19 ++++++++++++++++++- 9 | 2 files changed, 21 insertions(+), 1 deletion(-) 10 | 11 | diff --git a/config.def.h b/config.def.h 12 | index a2ac963..8123671 100644 13 | --- a/config.def.h 14 | +++ b/config.def.h 15 | @@ -21,6 +21,9 @@ static const char *colors[][3] = { 16 | /* tagging */ 17 | static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; 18 | 19 | +/* Lockfile */ 20 | +static char lockfile[] = "/tmp/dwm.lock"; 21 | + 22 | static const Rule rules[] = { 23 | /* xprop(1): 24 | * WM_CLASS(STRING) = instance, class 25 | diff --git a/dwm.c b/dwm.c 26 | index a96f33c..d55f186 100644 27 | --- a/dwm.c 28 | +++ b/dwm.c 29 | @@ -1252,7 +1252,24 @@ propertynotify(XEvent *e) 30 | void 31 | quit(const Arg *arg) 32 | { 33 | - running = 0; 34 | + FILE *fd = NULL; 35 | + struct stat filestat; 36 | + 37 | + if ((fd = fopen(lockfile, "r")) && stat(lockfile, &filestat) == 0) { 38 | + fclose(fd); 39 | + 40 | + if (filestat.st_ctime <= time(NULL)-2) 41 | + remove(lockfile); 42 | + } 43 | + 44 | + if ((fd = fopen(lockfile, "r")) != NULL) { 45 | + fclose(fd); 46 | + remove(lockfile); 47 | + running = 0; 48 | + } else { 49 | + if ((fd = fopen(lockfile, "a")) != NULL) 50 | + fclose(fd); 51 | + } 52 | } 53 | 54 | Monitor * 55 | -- 56 | 2.35.1 57 | 58 | -------------------------------------------------------------------------------- /patch/dwm-fancybar-20200423-ed3ab6b.diff: -------------------------------------------------------------------------------- 1 | diff --git a/dwm.c b/dwm.c 2 | index fb1e326..941293e 100644 3 | --- a/dwm.c 4 | +++ b/dwm.c 5 | @@ -696,10 +696,10 @@ dirtomon(int dir) 6 | void 7 | drawbar(Monitor *m) 8 | { 9 | - int x, w, tw = 0; 10 | + int x, w, tw = 0, mw, ew = 0; 11 | int boxs = drw->fonts->h / 9; 12 | int boxw = drw->fonts->h / 6 + 2; 13 | - unsigned int i, occ = 0, urg = 0; 14 | + unsigned int i, occ = 0, urg = 0, n = 0; 15 | Client *c; 16 | 17 | /* draw status first so it can be overdrawn by tags later */ 18 | @@ -710,6 +710,8 @@ drawbar(Monitor *m) 19 | } 20 | 21 | for (c = m->clients; c; c = c->next) { 22 | + if (ISVISIBLE(c)) 23 | + n++; 24 | occ |= c->tags; 25 | if (c->isurgent) 26 | urg |= c->tags; 27 | @@ -730,15 +732,39 @@ drawbar(Monitor *m) 28 | x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); 29 | 30 | if ((w = m->ww - tw - x) > bh) { 31 | - if (m->sel) { 32 | - drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); 33 | - drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); 34 | - if (m->sel->isfloating) 35 | - drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); 36 | - } else { 37 | - drw_setscheme(drw, scheme[SchemeNorm]); 38 | - drw_rect(drw, x, 0, w, bh, 1, 1); 39 | + if (n > 0) { 40 | + tw = TEXTW(m->sel->name) + lrpad; 41 | + mw = (tw >= w || n == 1) ? 0 : (w - tw) / (n - 1); 42 | + 43 | + i = 0; 44 | + for (c = m->clients; c; c = c->next) { 45 | + if (!ISVISIBLE(c) || c == m->sel) 46 | + continue; 47 | + tw = TEXTW(c->name); 48 | + if(tw < mw) 49 | + ew += (mw - tw); 50 | + else 51 | + i++; 52 | + } 53 | + if (i > 0) 54 | + mw += ew / i; 55 | + 56 | + for (c = m->clients; c; c = c->next) { 57 | + if (!ISVISIBLE(c)) 58 | + continue; 59 | + tw = MIN(m->sel == c ? w : mw, TEXTW(c->name)); 60 | + 61 | + drw_setscheme(drw, scheme[m->sel == c ? SchemeSel : SchemeNorm]); 62 | + if (tw > 0) /* trap special handling of 0 in drw_text */ 63 | + drw_text(drw, x, 0, tw, bh, lrpad / 2, c->name, 0); 64 | + if (c->isfloating) 65 | + drw_rect(drw, x + boxs, boxs, boxw, boxw, c->isfixed, 0); 66 | + x += tw; 67 | + w -= tw; 68 | + } 69 | } 70 | + drw_setscheme(drw, scheme[SchemeNorm]); 71 | + drw_rect(drw, x, 0, w, bh, 1, 1); 72 | } 73 | drw_map(drw, m->barwin, 0, 0, m->ww, bh); 74 | } 75 | -------------------------------------------------------------------------------- /patch/dwm-focusdir-6.3.diff: -------------------------------------------------------------------------------- 1 | From 45957f20d45e99469e7f296231769966b016d11a Mon Sep 17 00:00:00 2001 2 | From: Bakkeby 3 | Date: Mon, 10 Jan 2022 11:38:04 +0100 4 | Subject: [PATCH] focusdir: focus on the next client by direction (up, down, 5 | left, right) 6 | 7 | --- 8 | config.def.h | 4 ++++ 9 | dwm.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 10 | 2 files changed, 71 insertions(+) 11 | 12 | diff --git a/config.def.h b/config.def.h 13 | index a2ac963..cd41574 100644 14 | --- a/config.def.h 15 | +++ b/config.def.h 16 | @@ -67,6 +67,10 @@ static Key keys[] = { 17 | { MODKEY, XK_b, togglebar, {0} }, 18 | { MODKEY, XK_j, focusstack, {.i = +1 } }, 19 | { MODKEY, XK_k, focusstack, {.i = -1 } }, 20 | + { MODKEY, XK_Left, focusdir, {.i = 0 } }, // left 21 | + { MODKEY, XK_Right, focusdir, {.i = 1 } }, // right 22 | + { MODKEY, XK_Up, focusdir, {.i = 2 } }, // up 23 | + { MODKEY, XK_Down, focusdir, {.i = 3 } }, // down 24 | { MODKEY, XK_i, incnmaster, {.i = +1 } }, 25 | { MODKEY, XK_d, incnmaster, {.i = -1 } }, 26 | { MODKEY, XK_h, setmfact, {.f = -0.05} }, 27 | diff --git a/dwm.c b/dwm.c 28 | index a96f33c..c3a868e 100644 29 | --- a/dwm.c 30 | +++ b/dwm.c 31 | @@ -166,6 +166,7 @@ static void drawbars(void); 32 | static void enternotify(XEvent *e); 33 | static void expose(XEvent *e); 34 | static void focus(Client *c); 35 | +static void focusdir(const Arg *arg); 36 | static void focusin(XEvent *e); 37 | static void focusmon(const Arg *arg); 38 | static void focusstack(const Arg *arg); 39 | @@ -809,6 +810,72 @@ focus(Client *c) 40 | drawbars(); 41 | } 42 | 43 | +void 44 | +focusdir(const Arg *arg) 45 | +{ 46 | + Client *s = selmon->sel, *f = NULL, *c, *next; 47 | + 48 | + if (!s) 49 | + return; 50 | + 51 | + unsigned int score = -1; 52 | + unsigned int client_score; 53 | + int dist; 54 | + int dirweight = 20; 55 | + int isfloating = s->isfloating; 56 | + 57 | + next = s->next; 58 | + if (!next) 59 | + next = s->mon->clients; 60 | + for (c = next; c != s; c = next) { 61 | + 62 | + next = c->next; 63 | + if (!next) 64 | + next = s->mon->clients; 65 | + 66 | + if (!ISVISIBLE(c) || c->isfloating != isfloating) // || HIDDEN(c) 67 | + continue; 68 | + 69 | + switch (arg->i) { 70 | + case 0: // left 71 | + dist = s->x - c->x - c->w; 72 | + client_score = 73 | + dirweight * MIN(abs(dist), abs(dist + s->mon->ww)) + 74 | + abs(s->y - c->y); 75 | + break; 76 | + case 1: // right 77 | + dist = c->x - s->x - s->w; 78 | + client_score = 79 | + dirweight * MIN(abs(dist), abs(dist + s->mon->ww)) + 80 | + abs(c->y - s->y); 81 | + break; 82 | + case 2: // up 83 | + dist = s->y - c->y - c->h; 84 | + client_score = 85 | + dirweight * MIN(abs(dist), abs(dist + s->mon->wh)) + 86 | + abs(s->x - c->x); 87 | + break; 88 | + default: 89 | + case 3: // down 90 | + dist = c->y - s->y - s->h; 91 | + client_score = 92 | + dirweight * MIN(abs(dist), abs(dist + s->mon->wh)) + 93 | + abs(c->x - s->x); 94 | + break; 95 | + } 96 | + 97 | + if (((arg->i == 0 || arg->i == 2) && client_score <= score) || client_score < score) { 98 | + score = client_score; 99 | + f = c; 100 | + } 101 | + } 102 | + 103 | + if (f && f != s) { 104 | + focus(f); 105 | + restack(f->mon); 106 | + } 107 | +} 108 | + 109 | /* there are some broken focus acquiring clients needing extra handling */ 110 | void 111 | focusin(XEvent *e) 112 | -- 113 | 2.19.1 114 | -------------------------------------------------------------------------------- /patch/dwm-fullgaps-toggle-20200830.diff: -------------------------------------------------------------------------------- 1 | diff --git a/config.def.h b/config.def.h 2 | index 1c0b587..b172f63 100644 3 | --- a/config.def.h 4 | +++ b/config.def.h 5 | @@ -2,6 +2,7 @@ 6 | 7 | /* appearance */ 8 | static const unsigned int borderpx = 1; /* border pixel of windows */ 9 | +static const Gap default_gap = {.isgap = 1, .realgap = 10, .gappx = 10}; 10 | static const unsigned int snap = 32; /* snap pixel */ 11 | static const int showbar = 1; /* 0 means no bar */ 12 | static const int topbar = 1; /* 0 means bottom bar */ 13 | @@ -84,6 +85,10 @@ static Key keys[] = { 14 | { MODKEY, XK_period, focusmon, {.i = +1 } }, 15 | { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, 16 | { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, 17 | + { MODKEY, XK_minus, setgaps, {.i = -5 } }, 18 | + { MODKEY, XK_equal, setgaps, {.i = +5 } }, 19 | + { MODKEY|ShiftMask, XK_minus, setgaps, {.i = GAP_RESET } }, 20 | + { MODKEY|ShiftMask, XK_equal, setgaps, {.i = GAP_TOGGLE} }, 21 | TAGKEYS( XK_1, 0) 22 | TAGKEYS( XK_2, 1) 23 | TAGKEYS( XK_3, 2) 24 | diff --git a/dwm.c b/dwm.c 25 | index 664c527..25bc9b7 100644 26 | --- a/dwm.c 27 | +++ b/dwm.c 28 | @@ -57,6 +57,9 @@ 29 | #define TAGMASK ((1 << LENGTH(tags)) - 1) 30 | #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) 31 | 32 | +#define GAP_TOGGLE 100 33 | +#define GAP_RESET 0 34 | + 35 | /* enums */ 36 | enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ 37 | enum { SchemeNorm, SchemeSel }; /* color schemes */ 38 | @@ -111,6 +114,12 @@ typedef struct { 39 | void (*arrange)(Monitor *); 40 | } Layout; 41 | 42 | +typedef struct { 43 | + int isgap; 44 | + int realgap; 45 | + int gappx; 46 | +} Gap; 47 | + 48 | struct Monitor { 49 | char ltsymbol[16]; 50 | float mfact; 51 | @@ -119,6 +128,7 @@ struct Monitor { 52 | int by; /* bar geometry */ 53 | int mx, my, mw, mh; /* screen size */ 54 | int wx, wy, ww, wh; /* window area */ 55 | + Gap *gap; 56 | unsigned int seltags; 57 | unsigned int sellt; 58 | unsigned int tagset[2]; 59 | @@ -169,6 +179,7 @@ static void focus(Client *c); 60 | static void focusin(XEvent *e); 61 | static void focusmon(const Arg *arg); 62 | static void focusstack(const Arg *arg); 63 | +static void gap_copy(Gap *to, const Gap *from); 64 | static Atom getatomprop(Client *c, Atom prop); 65 | static int getrootptr(int *x, int *y); 66 | static long getstate(Window w); 67 | @@ -200,6 +211,7 @@ static void sendmon(Client *c, Monitor *m); 68 | static void setclientstate(Client *c, long state); 69 | static void setfocus(Client *c); 70 | static void setfullscreen(Client *c, int fullscreen); 71 | +static void setgaps(const Arg *arg); 72 | static void setlayout(const Arg *arg); 73 | static void setmfact(const Arg *arg); 74 | static void setup(void); 75 | @@ -639,6 +651,8 @@ createmon(void) 76 | m->nmaster = nmaster; 77 | m->showbar = showbar; 78 | m->topbar = topbar; 79 | + m->gap = malloc(sizeof(Gap)); 80 | + gap_copy(m->gap, &default_gap); 81 | m->lt[0] = &layouts[0]; 82 | m->lt[1] = &layouts[1 % LENGTH(layouts)]; 83 | strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); 84 | @@ -1498,6 +1512,35 @@ setfullscreen(Client *c, int fullscreen) 85 | } 86 | } 87 | 88 | +void 89 | +gap_copy(Gap *to, const Gap *from) 90 | +{ 91 | + to->isgap = from->isgap; 92 | + to->realgap = from->realgap; 93 | + to->gappx = from->gappx; 94 | +} 95 | + 96 | +void 97 | +setgaps(const Arg *arg) 98 | +{ 99 | + Gap *p = selmon->gap; 100 | + switch(arg->i) 101 | + { 102 | + case GAP_TOGGLE: 103 | + p->isgap = 1 - p->isgap; 104 | + break; 105 | + case GAP_RESET: 106 | + gap_copy(p, &default_gap); 107 | + break; 108 | + default: 109 | + p->realgap += arg->i; 110 | + p->isgap = 1; 111 | + } 112 | + p->realgap = MAX(p->realgap, 0); 113 | + p->gappx = p->realgap * p->isgap; 114 | + arrange(selmon); 115 | +} 116 | + 117 | void 118 | setlayout(const Arg *arg) 119 | { 120 | @@ -1684,18 +1727,18 @@ tile(Monitor *m) 121 | if (n > m->nmaster) 122 | mw = m->nmaster ? m->ww * m->mfact : 0; 123 | else 124 | - mw = m->ww; 125 | - for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) 126 | + mw = m->ww - m->gap->gappx; 127 | + for (i = 0, my = ty = m->gap->gappx, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) 128 | if (i < m->nmaster) { 129 | - h = (m->wh - my) / (MIN(n, m->nmaster) - i); 130 | - resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); 131 | - if (my + HEIGHT(c) < m->wh) 132 | - my += HEIGHT(c); 133 | + h = (m->wh - my) / (MIN(n, m->nmaster) - i) - m->gap->gappx; 134 | + resize(c, m->wx + m->gap->gappx, m->wy + my, mw - (2*c->bw) - m->gap->gappx, h - (2*c->bw), 0); 135 | + if (my + HEIGHT(c) + m->gap->gappx < m->wh) 136 | + my += HEIGHT(c) + m->gap->gappx; 137 | } else { 138 | - h = (m->wh - ty) / (n - i); 139 | - resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); 140 | - if (ty + HEIGHT(c) < m->wh) 141 | - ty += HEIGHT(c); 142 | + h = (m->wh - ty) / (n - i) - m->gap->gappx; 143 | + resize(c, m->wx + mw + m->gap->gappx, m->wy + ty, m->ww - mw - (2*c->bw) - 2*m->gap->gappx, h - (2*c->bw), 0); 144 | + if (ty + HEIGHT(c) + m->gap->gappx < m->wh) 145 | + ty += HEIGHT(c) + m->gap->gappx; 146 | } 147 | } 148 | 149 | -------------------------------------------------------------------------------- /patch/dwm-gaplessgrid-20160731-56a31dc.diff: -------------------------------------------------------------------------------- 1 | URL: http://dwm.suckless.org/patches/gapless_grid 2 | Add gapless grid layout. 3 | 4 | Index: dwm/gaplessgrid.c 5 | =================================================================== 6 | --- /dev/null 7 | +++ dwm/gaplessgrid.c 8 | @@ -0,0 +1,35 @@ 9 | +void 10 | +gaplessgrid(Monitor *m) { 11 | + unsigned int n, cols, rows, cn, rn, i, cx, cy, cw, ch; 12 | + Client *c; 13 | + 14 | + for(n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) ; 15 | + if(n == 0) 16 | + return; 17 | + 18 | + /* grid dimensions */ 19 | + for(cols = 0; cols <= n/2; cols++) 20 | + if(cols*cols >= n) 21 | + break; 22 | + if(n == 5) /* set layout against the general calculation: not 1:2:2, but 2:3 */ 23 | + cols = 2; 24 | + rows = n/cols; 25 | + 26 | + /* window geometries */ 27 | + cw = cols ? m->ww / cols : m->ww; 28 | + cn = 0; /* current column number */ 29 | + rn = 0; /* current row number */ 30 | + for(i = 0, c = nexttiled(m->clients); c; i++, c = nexttiled(c->next)) { 31 | + if(i/rows + 1 > cols - n%cols) 32 | + rows = n/cols + 1; 33 | + ch = rows ? m->wh / rows : m->wh; 34 | + cx = m->wx + cn*cw; 35 | + cy = m->wy + rn*ch; 36 | + resize(c, cx, cy, cw - 2 * c->bw, ch - 2 * c->bw, False); 37 | + rn++; 38 | + if(rn >= rows) { 39 | + rn = 0; 40 | + cn++; 41 | + } 42 | + } 43 | +} 44 | -------------------------------------------------------------------------------- /patch/dwm-hide_vacant_tags-6.3.diff: -------------------------------------------------------------------------------- 1 | diff --git a/dwm.c b/dwm.c 2 | index a96f33c..f2da729 100644 3 | --- a/dwm.c 4 | +++ b/dwm.c 5 | @@ -432,9 +432,15 @@ buttonpress(XEvent *e) 6 | } 7 | if (ev->window == selmon->barwin) { 8 | i = x = 0; 9 | - do 10 | + unsigned int occ = 0; 11 | + for(c = m->clients; c; c=c->next) 12 | + occ |= c->tags; 13 | + do { 14 | + /* Do not reserve space for vacant tags */ 15 | + if (!(occ & 1 << i || m->tagset[m->seltags] & 1 << i)) 16 | + continue; 17 | x += TEXTW(tags[i]); 18 | - while (ev->x >= x && ++i < LENGTH(tags)); 19 | + } while (ev->x >= x && ++i < LENGTH(tags)); 20 | if (i < LENGTH(tags)) { 21 | click = ClkTagBar; 22 | arg.ui = 1 << i; 23 | @@ -719,13 +725,12 @@ drawbar(Monitor *m) 24 | } 25 | x = 0; 26 | for (i = 0; i < LENGTH(tags); i++) { 27 | + /* Do not draw vacant tags */ 28 | + if(!(occ & 1 << i || m->tagset[m->seltags] & 1 << i)) 29 | + continue; 30 | w = TEXTW(tags[i]); 31 | drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); 32 | drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); 33 | - if (occ & 1 << i) 34 | - drw_rect(drw, x + boxs, boxs, boxw, boxw, 35 | - m == selmon && selmon->sel && selmon->sel->tags & 1 << i, 36 | - urg & 1 << i); 37 | x += w; 38 | } 39 | w = blw = TEXTW(m->ltsymbol); 40 | -------------------------------------------------------------------------------- /patch/dwm-launchers-20200527-f09418b.diff: -------------------------------------------------------------------------------- 1 | From 6b5e23cdf8108a9033acc7c21c8926c0c72647fc Mon Sep 17 00:00:00 2001 2 | From: Adham Zahran 3 | Date: Wed, 27 May 2020 18:07:57 +0200 4 | Subject: [PATCH] Top bar now has buttons that launches programs 5 | 6 | --- 7 | config.def.h | 8 ++++++++ 8 | dwm.c | 36 ++++++++++++++++++++++++++++++++++-- 9 | 2 files changed, 42 insertions(+), 2 deletions(-) 10 | 11 | diff --git a/config.def.h b/config.def.h 12 | index 1c0b587..9231cd5 100644 13 | --- a/config.def.h 14 | +++ b/config.def.h 15 | @@ -21,6 +21,14 @@ static const char *colors[][3] = { 16 | /* tagging */ 17 | static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; 18 | 19 | +/* launcher commands (They must be NULL terminated) */ 20 | +static const char* surf[] = { "surf", "duckduckgo.com", NULL }; 21 | + 22 | +static const Launcher launchers[] = { 23 | + /* command name to display */ 24 | + { surf, "surf" }, 25 | +}; 26 | + 27 | static const Rule rules[] = { 28 | /* xprop(1): 29 | * WM_CLASS(STRING) = instance, class 30 | diff --git a/dwm.c b/dwm.c 31 | index 9fd0286..79e7e20 100644 32 | --- a/dwm.c 33 | +++ b/dwm.c 34 | @@ -141,6 +141,11 @@ typedef struct { 35 | int monitor; 36 | } Rule; 37 | 38 | +typedef struct { 39 | + const char** command; 40 | + const char* name; 41 | +} Launcher; 42 | + 43 | /* function declarations */ 44 | static void applyrules(Client *c); 45 | static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); 46 | @@ -438,9 +443,26 @@ buttonpress(XEvent *e) 47 | if (i < LENGTH(tags)) { 48 | click = ClkTagBar; 49 | arg.ui = 1 << i; 50 | - } else if (ev->x < x + blw) 51 | + goto execute_handler; 52 | + } else if (ev->x < x + blw) { 53 | click = ClkLtSymbol; 54 | - else if (ev->x > selmon->ww - TEXTW(stext)) 55 | + goto execute_handler; 56 | + } 57 | + 58 | + x += blw; 59 | + 60 | + for(i = 0; i < LENGTH(launchers); i++) { 61 | + x += TEXTW(launchers[i].name); 62 | + 63 | + if (ev->x < x) { 64 | + Arg a; 65 | + a.v = launchers[i].command; 66 | + spawn(&a); 67 | + return; 68 | + } 69 | + } 70 | + 71 | + if (ev->x > selmon->ww - TEXTW(stext)) 72 | click = ClkStatusText; 73 | else 74 | click = ClkWinTitle; 75 | @@ -450,6 +472,9 @@ buttonpress(XEvent *e) 76 | XAllowEvents(dpy, ReplayPointer, CurrentTime); 77 | click = ClkClientWin; 78 | } 79 | + 80 | +execute_handler: 81 | + 82 | for (i = 0; i < LENGTH(buttons); i++) 83 | if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button 84 | && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) 85 | @@ -728,6 +753,13 @@ drawbar(Monitor *m) 86 | w = blw = TEXTW(m->ltsymbol); 87 | drw_setscheme(drw, scheme[SchemeNorm]); 88 | x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); 89 | + 90 | + for (i = 0; i < LENGTH(launchers); i++) 91 | + { 92 | + w = TEXTW(launchers[i].name); 93 | + drw_text(drw, x, 0, w, bh, lrpad / 2, launchers[i].name, urg & 1 << i); 94 | + x += w; 95 | + } 96 | 97 | if ((w = m->ww - tw - x) > bh) { 98 | if (m->sel) { 99 | -- 100 | 2.17.1 101 | 102 | -------------------------------------------------------------------------------- /patch/dwm-pertag-perseltag-6.2.diff: -------------------------------------------------------------------------------- 1 | diff -up a/dwm.c b/dwm.c 2 | --- a/dwm.c 2020-05-23 00:20:34.877944603 +0200 3 | +++ b/dwm.c 2020-06-22 12:49:55.298859682 +0200 4 | @@ -111,6 +111,7 @@ typedef struct { 5 | void (*arrange)(Monitor *); 6 | } Layout; 7 | 8 | +typedef struct Pertag Pertag; 9 | struct Monitor { 10 | char ltsymbol[16]; 11 | float mfact; 12 | @@ -130,6 +131,7 @@ struct Monitor { 13 | Monitor *next; 14 | Window barwin; 15 | const Layout *lt[2]; 16 | + Pertag *pertag; 17 | }; 18 | 19 | typedef struct { 20 | @@ -271,6 +273,15 @@ static Window root, wmcheckwin; 21 | /* configuration, allows nested code to access above variables */ 22 | #include "config.h" 23 | 24 | +struct Pertag { 25 | + unsigned int curtag, prevtag; /* current and previous tag */ 26 | + int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ 27 | + float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */ 28 | + unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */ 29 | + const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */ 30 | + int showbars[LENGTH(tags) + 1]; /* display bar for the current tag */ 31 | +}; 32 | + 33 | /* compile-time check if all tags fit into an unsigned int bit array. */ 34 | struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; 35 | 36 | @@ -631,6 +642,7 @@ Monitor * 37 | createmon(void) 38 | { 39 | Monitor *m; 40 | + unsigned int i; 41 | 42 | m = ecalloc(1, sizeof(Monitor)); 43 | m->tagset[0] = m->tagset[1] = 1; 44 | @@ -641,6 +653,20 @@ createmon(void) 45 | m->lt[0] = &layouts[0]; 46 | m->lt[1] = &layouts[1 % LENGTH(layouts)]; 47 | strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); 48 | + m->pertag = ecalloc(1, sizeof(Pertag)); 49 | + m->pertag->curtag = m->pertag->prevtag = 1; 50 | + 51 | + for (i = 0; i <= LENGTH(tags); i++) { 52 | + m->pertag->nmasters[i] = m->nmaster; 53 | + m->pertag->mfacts[i] = m->mfact; 54 | + 55 | + m->pertag->ltidxs[i][0] = m->lt[0]; 56 | + m->pertag->ltidxs[i][1] = m->lt[1]; 57 | + m->pertag->sellts[i] = m->sellt; 58 | + 59 | + m->pertag->showbars[i] = m->showbar; 60 | + } 61 | + 62 | return m; 63 | } 64 | 65 | @@ -966,7 +992,16 @@ grabkeys(void) 66 | void 67 | incnmaster(const Arg *arg) 68 | { 69 | + unsigned int i; 70 | selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); 71 | + for(i=0; itagset[selmon->seltags] & 1<pertag->nmasters[i+1] = selmon->nmaster; 74 | + 75 | + if(selmon->pertag->curtag == 0) 76 | + { 77 | + selmon->pertag->nmasters[0] = selmon->nmaster; 78 | + } 79 | arrange(selmon); 80 | } 81 | 82 | @@ -1500,11 +1535,26 @@ setfullscreen(Client *c, int fullscreen) 83 | void 84 | setlayout(const Arg *arg) 85 | { 86 | + unsigned int i; 87 | if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) 88 | selmon->sellt ^= 1; 89 | if (arg && arg->v) 90 | selmon->lt[selmon->sellt] = (Layout *)arg->v; 91 | strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); 92 | + 93 | + for(i=0; itagset[selmon->seltags] & 1<pertag->ltidxs[i+1][selmon->sellt] = selmon->lt[selmon->sellt]; 97 | + selmon->pertag->sellts[i+1] = selmon->sellt; 98 | + } 99 | + 100 | + if(selmon->pertag->curtag == 0) 101 | + { 102 | + selmon->pertag->ltidxs[0][selmon->sellt] = selmon->lt[selmon->sellt]; 103 | + selmon->pertag->sellts[0] = selmon->sellt; 104 | + } 105 | + 106 | if (selmon->sel) 107 | arrange(selmon); 108 | else 109 | @@ -1516,13 +1566,24 @@ void 110 | setmfact(const Arg *arg) 111 | { 112 | float f; 113 | + unsigned int i; 114 | 115 | if (!arg || !selmon->lt[selmon->sellt]->arrange) 116 | return; 117 | f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; 118 | - if (f < 0.1 || f > 0.9) 119 | + if (arg->f == 0.0) 120 | + f = mfact; 121 | + if (f < 0.05 || f > 0.95) 122 | return; 123 | selmon->mfact = f; 124 | + for(i=0; itagset[selmon->seltags] & 1<pertag->mfacts[i+1] = f; 127 | + 128 | + if(selmon->pertag->curtag == 0) 129 | + { 130 | + selmon->pertag->mfacts[0] = f; 131 | + } 132 | arrange(selmon); 133 | } 134 | 135 | @@ -1699,7 +1760,16 @@ tile(Monitor *m) 136 | void 137 | togglebar(const Arg *arg) 138 | { 139 | + unsigned int i; 140 | selmon->showbar = !selmon->showbar; 141 | + for(i=0; itagset[selmon->seltags] & 1<pertag->showbars[i+1] = selmon->showbar; 144 | + 145 | + if(selmon->pertag->curtag == 0) 146 | + { 147 | + selmon->pertag->showbars[0] = selmon->showbar; 148 | + } 149 | updatebarpos(selmon); 150 | XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); 151 | arrange(selmon); 152 | @@ -1738,9 +1808,33 @@ void 153 | toggleview(const Arg *arg) 154 | { 155 | unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); 156 | + int i; 157 | 158 | if (newtagset) { 159 | selmon->tagset[selmon->seltags] = newtagset; 160 | + 161 | + if (newtagset == ~0) { 162 | + selmon->pertag->prevtag = selmon->pertag->curtag; 163 | + selmon->pertag->curtag = 0; 164 | + } 165 | + 166 | + /* test if the user did not select the same tag */ 167 | + if (!(newtagset & 1 << (selmon->pertag->curtag - 1))) { 168 | + selmon->pertag->prevtag = selmon->pertag->curtag; 169 | + for (i = 0; !(newtagset & 1 << i); i++) ; 170 | + selmon->pertag->curtag = i + 1; 171 | + } 172 | + 173 | + /* apply settings for this view */ 174 | + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; 175 | + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; 176 | + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; 177 | + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; 178 | + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; 179 | + 180 | + if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) 181 | + togglebar(NULL); 182 | + 183 | focus(NULL); 184 | arrange(selmon); 185 | } 186 | @@ -2035,11 +2129,37 @@ updatewmhints(Client *c) 187 | void 188 | view(const Arg *arg) 189 | { 190 | + int i; 191 | + unsigned int tmptag; 192 | + 193 | if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) 194 | return; 195 | selmon->seltags ^= 1; /* toggle sel tagset */ 196 | - if (arg->ui & TAGMASK) 197 | + if (arg->ui & TAGMASK) { 198 | selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; 199 | + selmon->pertag->prevtag = selmon->pertag->curtag; 200 | + 201 | + if (arg->ui == ~0) 202 | + selmon->pertag->curtag = 0; 203 | + else { 204 | + for (i = 0; !(arg->ui & 1 << i); i++) ; 205 | + selmon->pertag->curtag = i + 1; 206 | + } 207 | + } else { 208 | + tmptag = selmon->pertag->prevtag; 209 | + selmon->pertag->prevtag = selmon->pertag->curtag; 210 | + selmon->pertag->curtag = tmptag; 211 | + } 212 | + 213 | + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; 214 | + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; 215 | + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; 216 | + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; 217 | + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; 218 | + 219 | + if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) 220 | + togglebar(NULL); 221 | + 222 | focus(NULL); 223 | arrange(selmon); 224 | } 225 | -------------------------------------------------------------------------------- /patch/dwm-restartsig-20180523-6.2.diff: -------------------------------------------------------------------------------- 1 | From 2991f37f0aaf44b9f9b11e7893ff0af8eb88f649 Mon Sep 17 00:00:00 2001 2 | From: Christopher Drelich 3 | Date: Wed, 23 May 2018 22:50:38 -0400 4 | Subject: [PATCH] Modifies quit to handle restarts and adds SIGHUP and SIGTERM 5 | handlers. 6 | 7 | Modified quit() to restart if it receives arg .i = 1 8 | MOD+CTRL+SHIFT+Q was added to confid.def.h to do just that. 9 | 10 | Signal handlers were handled for SIGHUP and SIGTERM. 11 | If dwm receives these signals it calls quit() with 12 | arg .i = to 1 or 0, respectively. 13 | 14 | To restart dwm: 15 | MOD+CTRL+SHIFT+Q 16 | or 17 | kill -HUP dwmpid 18 | 19 | To quit dwm cleanly: 20 | MOD+SHIFT+Q 21 | or 22 | kill -TERM dwmpid 23 | --- 24 | config.def.h | 1 + 25 | dwm.1 | 10 ++++++++++ 26 | dwm.c | 22 ++++++++++++++++++++++ 27 | 3 files changed, 33 insertions(+) 28 | 29 | diff --git a/config.def.h b/config.def.h 30 | index a9ac303..e559429 100644 31 | --- a/config.def.h 32 | +++ b/config.def.h 33 | @@ -94,6 +94,7 @@ static Key keys[] = { 34 | TAGKEYS( XK_8, 7) 35 | TAGKEYS( XK_9, 8) 36 | { MODKEY|ShiftMask, XK_q, quit, {0} }, 37 | + { MODKEY|ControlMask|ShiftMask, XK_q, quit, {1} }, 38 | }; 39 | 40 | /* button definitions */ 41 | diff --git a/dwm.1 b/dwm.1 42 | index 13b3729..36a331c 100644 43 | --- a/dwm.1 44 | +++ b/dwm.1 45 | @@ -142,6 +142,9 @@ Add/remove all windows with nth tag to/from the view. 46 | .TP 47 | .B Mod1\-Shift\-q 48 | Quit dwm. 49 | +.TP 50 | +.B Mod1\-Control\-Shift\-q 51 | +Restart dwm. 52 | .SS Mouse commands 53 | .TP 54 | .B Mod1\-Button1 55 | @@ -155,6 +158,13 @@ Resize focused window while dragging. Tiled windows will be toggled to the float 56 | .SH CUSTOMIZATION 57 | dwm is customized by creating a custom config.h and (re)compiling the source 58 | code. This keeps it fast, secure and simple. 59 | +.SH SIGNALS 60 | +.TP 61 | +.B SIGHUP - 1 62 | +Restart the dwm process. 63 | +.TP 64 | +.B SIGTERM - 15 65 | +Cleanly terminate the dwm process. 66 | .SH SEE ALSO 67 | .BR dmenu (1), 68 | .BR st (1) 69 | diff --git a/dwm.c b/dwm.c 70 | index bb95e26..286eecd 100644 71 | --- a/dwm.c 72 | +++ b/dwm.c 73 | @@ -205,6 +205,8 @@ static void setup(void); 74 | static void seturgent(Client *c, int urg); 75 | static void showhide(Client *c); 76 | static void sigchld(int unused); 77 | +static void sighup(int unused); 78 | +static void sigterm(int unused); 79 | static void spawn(const Arg *arg); 80 | static void tag(const Arg *arg); 81 | static void tagmon(const Arg *arg); 82 | @@ -260,6 +262,7 @@ static void (*handler[LASTEvent]) (XEvent *) = { 83 | [UnmapNotify] = unmapnotify 84 | }; 85 | static Atom wmatom[WMLast], netatom[NetLast]; 86 | +static int restart = 0; 87 | static int running = 1; 88 | static Cur *cursor[CurLast]; 89 | static Clr **scheme; 90 | @@ -1248,6 +1251,7 @@ propertynotify(XEvent *e) 91 | void 92 | quit(const Arg *arg) 93 | { 94 | + if(arg->i) restart = 1; 95 | running = 0; 96 | } 97 | 98 | @@ -1536,6 +1540,9 @@ setup(void) 99 | /* clean up any zombies immediately */ 100 | sigchld(0); 101 | 102 | + signal(SIGHUP, sighup); 103 | + signal(SIGTERM, sigterm); 104 | + 105 | /* init screen */ 106 | screen = DefaultScreen(dpy); 107 | sw = DisplayWidth(dpy, screen); 108 | @@ -1637,6 +1644,20 @@ sigchld(int unused) 109 | } 110 | 111 | void 112 | +sighup(int unused) 113 | +{ 114 | + Arg a = {.i = 1}; 115 | + quit(&a); 116 | +} 117 | + 118 | +void 119 | +sigterm(int unused) 120 | +{ 121 | + Arg a = {.i = 0}; 122 | + quit(&a); 123 | +} 124 | + 125 | +void 126 | spawn(const Arg *arg) 127 | { 128 | if (arg->v == dmenucmd) 129 | @@ -2139,6 +2160,7 @@ main(int argc, char *argv[]) 130 | setup(); 131 | scan(); 132 | run(); 133 | + if(restart) execvp(argv[0], argv); 134 | cleanup(); 135 | XCloseDisplay(dpy); 136 | return EXIT_SUCCESS; 137 | -- 138 | 2.7.4 139 | 140 | -------------------------------------------------------------------------------- /patch/dwm-restoreafterrestart-20220709-d3f93c7.diff: -------------------------------------------------------------------------------- 1 | From 9fd4a02b57aa8a764d8105d5f2f854372f4ef559 Mon Sep 17 00:00:00 2001 2 | From: ViliamKovac1223 3 | Date: Sat, 9 Jul 2022 17:35:54 +0200 4 | Subject: [PATCH] add restore patch 5 | 6 | --- 7 | config.def.h | 2 ++ 8 | dwm.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 9 | 2 files changed, 55 insertions(+) 10 | 11 | diff --git a/config.def.h b/config.def.h 12 | index 6ec4146..0b91976 100644 13 | --- a/config.def.h 14 | +++ b/config.def.h 15 | @@ -1,5 +1,7 @@ 16 | /* See LICENSE file for copyright and license details. */ 17 | 18 | +#define SESSION_FILE "/tmp/dwm-session" 19 | + 20 | /* appearance */ 21 | static const unsigned int borderpx = 1; /* border pixel of windows */ 22 | static const unsigned int snap = 32; /* snap pixel */ 23 | diff --git a/dwm.c b/dwm.c 24 | index 74cec7e..76b40a2 100644 25 | --- a/dwm.c 26 | +++ b/dwm.c 27 | @@ -1255,11 +1255,63 @@ propertynotify(XEvent *e) 28 | } 29 | } 30 | 31 | +void 32 | +saveSession(void) 33 | +{ 34 | + FILE *fw = fopen(SESSION_FILE, "w"); 35 | + for (Client *c = selmon->clients; c != NULL; c = c->next) { // get all the clients with their tags and write them to the file 36 | + fprintf(fw, "%lu %u\n", c->win, c->tags); 37 | + } 38 | + fclose(fw); 39 | +} 40 | + 41 | +void 42 | +restoreSession(void) 43 | +{ 44 | + // restore session 45 | + FILE *fr = fopen(SESSION_FILE, "r"); 46 | + if (!fr) 47 | + return; 48 | + 49 | + char *str = malloc(23 * sizeof(char)); // allocate enough space for excepted input from text file 50 | + while (fscanf(fr, "%[^\n] ", str) != EOF) { // read file till the end 51 | + long unsigned int winId; 52 | + unsigned int tagsForWin; 53 | + int check = sscanf(str, "%lu %u", &winId, &tagsForWin); // get data 54 | + if (check != 2) // break loop if data wasn't read correctly 55 | + break; 56 | + 57 | + for (Client *c = selmon->clients; c ; c = c->next) { // add tags to every window by winId 58 | + if (c->win == winId) { 59 | + c->tags = tagsForWin; 60 | + break; 61 | + } 62 | + } 63 | + } 64 | + 65 | + for (Client *c = selmon->clients; c ; c = c->next) { // refocus on windows 66 | + focus(c); 67 | + restack(c->mon); 68 | + } 69 | + 70 | + for (Monitor *m = selmon; m; m = m->next) // rearrange all monitors 71 | + arrange(m); 72 | + 73 | + free(str); 74 | + fclose(fr); 75 | + 76 | + // delete a file 77 | + remove(SESSION_FILE); 78 | +} 79 | + 80 | void 81 | quit(const Arg *arg) 82 | { 83 | if(arg->i) restart = 1; 84 | running = 0; 85 | + 86 | + if (restart == 1) 87 | + saveSession(); 88 | } 89 | 90 | Monitor * 91 | @@ -2173,6 +2225,7 @@ main(int argc, char *argv[]) 92 | die("pledge"); 93 | #endif /* __OpenBSD__ */ 94 | scan(); 95 | + restoreSession(); 96 | run(); 97 | if(restart) execvp(argv[0], argv); 98 | cleanup(); 99 | -- 100 | 2.35.1 101 | 102 | -------------------------------------------------------------------------------- /patch/dwm-rotatestack-20161021-ab9571b.diff: -------------------------------------------------------------------------------- 1 | diff --git a/config.def.h b/config.def.h 2 | index fd77a07..09737d7 100644 3 | --- a/config.def.h 4 | +++ b/config.def.h 5 | @@ -64,6 +64,8 @@ static Key keys[] = { 6 | { MODKEY, XK_p, spawn, {.v = dmenucmd } }, 7 | { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, 8 | { MODKEY, XK_b, togglebar, {0} }, 9 | + { MODKEY|ShiftMask, XK_j, rotatestack, {.i = +1 } }, 10 | + { MODKEY|ShiftMask, XK_k, rotatestack, {.i = -1 } }, 11 | { MODKEY, XK_j, focusstack, {.i = +1 } }, 12 | { MODKEY, XK_k, focusstack, {.i = -1 } }, 13 | { MODKEY, XK_i, incnmaster, {.i = +1 } }, 14 | diff --git a/dwm.c b/dwm.c 15 | index 421bf27..1ec8b10 100644 16 | --- a/dwm.c 17 | +++ b/dwm.c 18 | @@ -165,6 +165,8 @@ static void detachstack(Client *c); 19 | static Monitor *dirtomon(int dir); 20 | static void drawbar(Monitor *m); 21 | static void drawbars(void); 22 | +static void enqueue(Client *c); 23 | +static void enqueuestack(Client *c); 24 | static void enternotify(XEvent *e); 25 | static void expose(XEvent *e); 26 | static void focus(Client *c); 27 | @@ -194,6 +196,7 @@ static void resize(Client *c, int x, int y, int w, int h, int interact); 28 | static void resizeclient(Client *c, int x, int y, int w, int h); 29 | static void resizemouse(const Arg *arg); 30 | static void restack(Monitor *m); 31 | +static void rotatestack(const Arg *arg); 32 | static void run(void); 33 | static void scan(void); 34 | static int sendevent(Client *c, Atom proto); 35 | @@ -765,6 +768,28 @@ drawbars(void) 36 | } 37 | 38 | void 39 | +enqueue(Client *c) 40 | +{ 41 | + Client *l; 42 | + for (l = c->mon->clients; l && l->next; l = l->next); 43 | + if (l) { 44 | + l->next = c; 45 | + c->next = NULL; 46 | + } 47 | +} 48 | + 49 | +void 50 | +enqueuestack(Client *c) 51 | +{ 52 | + Client *l; 53 | + for (l = c->mon->stack; l && l->snext; l = l->snext); 54 | + if (l) { 55 | + l->snext = c; 56 | + c->snext = NULL; 57 | + } 58 | +} 59 | + 60 | +void 61 | enternotify(XEvent *e) 62 | { 63 | Client *c; 64 | @@ -1390,6 +1415,38 @@ restack(Monitor *m) 65 | } 66 | 67 | void 68 | +rotatestack(const Arg *arg) 69 | +{ 70 | + Client *c = NULL, *f; 71 | + 72 | + if (!selmon->sel) 73 | + return; 74 | + f = selmon->sel; 75 | + if (arg->i > 0) { 76 | + for (c = nexttiled(selmon->clients); c && nexttiled(c->next); c = nexttiled(c->next)); 77 | + if (c){ 78 | + detach(c); 79 | + attach(c); 80 | + detachstack(c); 81 | + attachstack(c); 82 | + } 83 | + } else { 84 | + if ((c = nexttiled(selmon->clients))){ 85 | + detach(c); 86 | + enqueue(c); 87 | + detachstack(c); 88 | + enqueuestack(c); 89 | + } 90 | + } 91 | + if (c){ 92 | + arrange(selmon); 93 | + //unfocus(f, 1); 94 | + focus(f); 95 | + restack(selmon); 96 | + } 97 | +} 98 | + 99 | +void 100 | run(void) 101 | { 102 | XEvent ev; 103 | -------------------------------------------------------------------------------- /patch/dwm-scratchpad-6.2.diff: -------------------------------------------------------------------------------- 1 | diff -up a/config.def.h b/config.def.h 2 | --- a/config.def.h 2019-06-06 21:23:27.006661784 +0200 3 | +++ b/config.def.h 2019-06-20 15:05:59.083102462 +0200 4 | @@ -58,11 +58,14 @@ static const Layout layouts[] = { 5 | static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */ 6 | static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; 7 | static const char *termcmd[] = { "st", NULL }; 8 | +static const char scratchpadname[] = "scratchpad"; 9 | +static const char *scratchpadcmd[] = { "st", "-t", scratchpadname, "-g", "120x34", NULL }; 10 | 11 | static Key keys[] = { 12 | /* modifier key function argument */ 13 | { MODKEY, XK_p, spawn, {.v = dmenucmd } }, 14 | { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, 15 | + { MODKEY, XK_grave, togglescratch, {.v = scratchpadcmd } }, 16 | { MODKEY, XK_b, togglebar, {0} }, 17 | { MODKEY, XK_j, focusstack, {.i = +1 } }, 18 | { MODKEY, XK_k, focusstack, {.i = -1 } }, 19 | diff -up a/dwm.c b/dwm.c 20 | --- a/dwm.c 2019-06-06 21:23:27.023328450 +0200 21 | +++ b/dwm.c 2019-06-20 15:07:01.089767947 +0200 22 | @@ -213,6 +213,7 @@ static void tagmon(const Arg *arg); 23 | static void tile(Monitor *); 24 | static void togglebar(const Arg *arg); 25 | static void togglefloating(const Arg *arg); 26 | +static void togglescratch(const Arg *arg); 27 | static void toggletag(const Arg *arg); 28 | static void toggleview(const Arg *arg); 29 | static void unfocus(Client *c, int setfocus); 30 | @@ -273,6 +274,8 @@ static Window root, wmcheckwin; 31 | /* configuration, allows nested code to access above variables */ 32 | #include "config.h" 33 | 34 | +static unsigned int scratchtag = 1 << LENGTH(tags); 35 | + 36 | /* compile-time check if all tags fit into an unsigned int bit array. */ 37 | struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; 38 | 39 | @@ -1052,6 +1055,14 @@ manage(Window w, XWindowAttributes *wa) 40 | && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); 41 | c->bw = borderpx; 42 | 43 | + selmon->tagset[selmon->seltags] &= ~scratchtag; 44 | + if (!strcmp(c->name, scratchpadname)) { 45 | + c->mon->tagset[c->mon->seltags] |= c->tags = scratchtag; 46 | + c->isfloating = True; 47 | + c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); 48 | + c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); 49 | + } 50 | + 51 | wc.border_width = c->bw; 52 | XConfigureWindow(dpy, w, CWBorderWidth, &wc); 53 | XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); 54 | @@ -1661,6 +1672,7 @@ spawn(const Arg *arg) 55 | { 56 | if (arg->v == dmenucmd) 57 | dmenumon[0] = '0' + selmon->num; 58 | + selmon->tagset[selmon->seltags] &= ~scratchtag; 59 | if (fork() == 0) { 60 | if (dpy) 61 | close(ConnectionNumber(dpy)); 62 | @@ -1748,6 +1760,28 @@ togglefloating(const Arg *arg) 63 | } 64 | 65 | void 66 | +togglescratch(const Arg *arg) 67 | +{ 68 | + Client *c; 69 | + unsigned int found = 0; 70 | + 71 | + for (c = selmon->clients; c && !(found = c->tags & scratchtag); c = c->next); 72 | + if (found) { 73 | + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ scratchtag; 74 | + if (newtagset) { 75 | + selmon->tagset[selmon->seltags] = newtagset; 76 | + focus(NULL); 77 | + arrange(selmon); 78 | + } 79 | + if (ISVISIBLE(c)) { 80 | + focus(c); 81 | + restack(selmon); 82 | + } 83 | + } else 84 | + spawn(arg); 85 | +} 86 | + 87 | +void 88 | toggletag(const Arg *arg) 89 | { 90 | unsigned int newtags; 91 | -------------------------------------------------------------------------------- /patch/dwm-statuscmd-20210405-67d76bd.diff: -------------------------------------------------------------------------------- 1 | From f58c7e4fd05ec13383518ccd51663167d45e92d0 Mon Sep 17 00:00:00 2001 2 | From: Daniel Bylinka 3 | Date: Fri, 2 Apr 2021 19:02:58 +0200 4 | Subject: [PATCH] [statuscmd] Signal mouse button and click location to status 5 | monitor 6 | 7 | --- 8 | config.def.h | 6 +++- 9 | dwm.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++--- 10 | 2 files changed, 100 insertions(+), 6 deletions(-) 11 | 12 | diff --git a/config.def.h b/config.def.h 13 | index 1c0b587..154a59b 100644 14 | --- a/config.def.h 15 | +++ b/config.def.h 16 | @@ -54,6 +54,8 @@ static const Layout layouts[] = { 17 | /* helper for spawning shell commands in the pre dwm-5.0 fashion */ 18 | #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } 19 | 20 | +#define STATUSBAR "dwmblocks" 21 | + 22 | /* commands */ 23 | static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */ 24 | static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; 25 | @@ -103,7 +105,9 @@ static Button buttons[] = { 26 | { ClkLtSymbol, 0, Button1, setlayout, {0} }, 27 | { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, 28 | { ClkWinTitle, 0, Button2, zoom, {0} }, 29 | - { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, 30 | + { ClkStatusText, 0, Button1, sigstatusbar, {.i = 1} }, 31 | + { ClkStatusText, 0, Button2, sigstatusbar, {.i = 2} }, 32 | + { ClkStatusText, 0, Button3, sigstatusbar, {.i = 3} }, 33 | { ClkClientWin, MODKEY, Button1, movemouse, {0} }, 34 | { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, 35 | { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, 36 | diff --git a/dwm.c b/dwm.c 37 | index b0b3466..d871457 100644 38 | --- a/dwm.c 39 | +++ b/dwm.c 40 | @@ -172,6 +172,7 @@ static void focusstack(const Arg *arg); 41 | static Atom getatomprop(Client *c, Atom prop); 42 | static int getrootptr(int *x, int *y); 43 | static long getstate(Window w); 44 | +static pid_t getstatusbarpid(); 45 | static int gettextprop(Window w, Atom atom, char *text, unsigned int size); 46 | static void grabbuttons(Client *c, int focused); 47 | static void grabkeys(void); 48 | @@ -206,6 +207,7 @@ static void setup(void); 49 | static void seturgent(Client *c, int urg); 50 | static void showhide(Client *c); 51 | static void sigchld(int unused); 52 | +static void sigstatusbar(const Arg *arg); 53 | static void spawn(const Arg *arg); 54 | static void tag(const Arg *arg); 55 | static void tagmon(const Arg *arg); 56 | @@ -238,6 +240,9 @@ static void zoom(const Arg *arg); 57 | /* variables */ 58 | static const char broken[] = "broken"; 59 | static char stext[256]; 60 | +static int statusw; 61 | +static int statussig; 62 | +static pid_t statuspid = -1; 63 | static int screen; 64 | static int sw, sh; /* X display screen geometry width, height */ 65 | static int bh, blw = 0; /* bar geometry */ 66 | @@ -422,6 +427,7 @@ buttonpress(XEvent *e) 67 | Client *c; 68 | Monitor *m; 69 | XButtonPressedEvent *ev = &e->xbutton; 70 | + char *text, *s, ch; 71 | 72 | click = ClkRootWin; 73 | /* focus monitor if necessary */ 74 | @@ -440,9 +446,23 @@ buttonpress(XEvent *e) 75 | arg.ui = 1 << i; 76 | } else if (ev->x < x + blw) 77 | click = ClkLtSymbol; 78 | - else if (ev->x > selmon->ww - (int)TEXTW(stext)) 79 | + else if (ev->x > selmon->ww - statusw) { 80 | + x = selmon->ww - statusw; 81 | click = ClkStatusText; 82 | - else 83 | + statussig = 0; 84 | + for (text = s = stext; *s && x <= ev->x; s++) { 85 | + if ((unsigned char)(*s) < ' ') { 86 | + ch = *s; 87 | + *s = '\0'; 88 | + x += TEXTW(text) - lrpad; 89 | + *s = ch; 90 | + text = s + 1; 91 | + if (x >= ev->x) 92 | + break; 93 | + statussig = ch; 94 | + } 95 | + } 96 | + } else 97 | click = ClkWinTitle; 98 | } else if ((c = wintoclient(ev->window))) { 99 | focus(c); 100 | @@ -704,9 +724,24 @@ drawbar(Monitor *m) 101 | 102 | /* draw status first so it can be overdrawn by tags later */ 103 | if (m == selmon) { /* status is only drawn on selected monitor */ 104 | + char *text, *s, ch; 105 | drw_setscheme(drw, scheme[SchemeNorm]); 106 | - tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ 107 | - drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); 108 | + 109 | + x = 0; 110 | + for (text = s = stext; *s; s++) { 111 | + if ((unsigned char)(*s) < ' ') { 112 | + ch = *s; 113 | + *s = '\0'; 114 | + tw = TEXTW(text) - lrpad; 115 | + drw_text(drw, m->ww - statusw + x, 0, tw, bh, 0, text, 0); 116 | + x += tw; 117 | + *s = ch; 118 | + text = s + 1; 119 | + } 120 | + } 121 | + tw = TEXTW(text) - lrpad + 2; 122 | + drw_text(drw, m->ww - statusw + x, 0, tw, bh, 0, text, 0); 123 | + tw = statusw; 124 | } 125 | 126 | for (c = m->clients; c; c = c->next) { 127 | @@ -872,6 +907,30 @@ getatomprop(Client *c, Atom prop) 128 | return atom; 129 | } 130 | 131 | +pid_t 132 | +getstatusbarpid() 133 | +{ 134 | + char buf[32], *str = buf, *c; 135 | + FILE *fp; 136 | + 137 | + if (statuspid > 0) { 138 | + snprintf(buf, sizeof(buf), "/proc/%u/cmdline", statuspid); 139 | + if ((fp = fopen(buf, "r"))) { 140 | + fgets(buf, sizeof(buf), fp); 141 | + while ((c = strchr(str, '/'))) 142 | + str = c + 1; 143 | + fclose(fp); 144 | + if (!strcmp(str, STATUSBAR)) 145 | + return statuspid; 146 | + } 147 | + } 148 | + if (!(fp = popen("pidof -s "STATUSBAR, "r"))) 149 | + return -1; 150 | + fgets(buf, sizeof(buf), fp); 151 | + pclose(fp); 152 | + return strtol(buf, NULL, 10); 153 | +} 154 | + 155 | int 156 | getrootptr(int *x, int *y) 157 | { 158 | @@ -1637,6 +1696,20 @@ sigchld(int unused) 159 | while (0 < waitpid(-1, NULL, WNOHANG)); 160 | } 161 | 162 | +void 163 | +sigstatusbar(const Arg *arg) 164 | +{ 165 | + union sigval sv; 166 | + 167 | + if (!statussig) 168 | + return; 169 | + sv.sival_int = arg->i; 170 | + if ((statuspid = getstatusbarpid()) <= 0) 171 | + return; 172 | + 173 | + sigqueue(statuspid, SIGRTMIN+statussig, sv); 174 | +} 175 | + 176 | void 177 | spawn(const Arg *arg) 178 | { 179 | @@ -1990,8 +2063,25 @@ updatesizehints(Client *c) 180 | void 181 | updatestatus(void) 182 | { 183 | - if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) 184 | + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) { 185 | strcpy(stext, "dwm-"VERSION); 186 | + statusw = TEXTW(stext) - lrpad + 2; 187 | + } else { 188 | + char *text, *s, ch; 189 | + 190 | + statusw = 0; 191 | + for (text = s = stext; *s; s++) { 192 | + if ((unsigned char)(*s) < ' ') { 193 | + ch = *s; 194 | + *s = '\0'; 195 | + statusw += TEXTW(text) - lrpad; 196 | + *s = ch; 197 | + text = s + 1; 198 | + } 199 | + } 200 | + statusw += TEXTW(text) - lrpad + 2; 201 | + 202 | + } 203 | drawbar(selmon); 204 | } 205 | 206 | -- 207 | 2.31.0 208 | 209 | -------------------------------------------------------------------------------- /patch/dwm-systray-6.3.diff: -------------------------------------------------------------------------------- 1 | diff --git a/config.def.h b/config.def.h 2 | index a2ac963..4be4c06 100644 3 | --- a/config.def.h 4 | +++ b/config.def.h 5 | @@ -2,9 +2,14 @@ 6 | 7 | /* appearance */ 8 | static const unsigned int borderpx = 1; /* border pixel of windows */ 9 | -static const unsigned int snap = 32; /* snap pixel */ 10 | -static const int showbar = 1; /* 0 means no bar */ 11 | -static const int topbar = 1; /* 0 means bottom bar */ 12 | +static const unsigned int snap = 32; /* snap pixel */ 13 | +static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */ 14 | +static const unsigned int systrayonleft = 0; /* 0: systray in the right corner, >0: systray on left of status text */ 15 | +static const unsigned int systrayspacing = 2; /* systray spacing */ 16 | +static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/ 17 | +static const int showsystray = 1; /* 0 means no systray */ 18 | +static const int showbar = 1; /* 0 means no bar */ 19 | +static const int topbar = 1; /* 0 means bottom bar */ 20 | static const char *fonts[] = { "monospace:size=10" }; 21 | static const char dmenufont[] = "monospace:size=10"; 22 | static const char col_gray1[] = "#222222"; 23 | @@ -101,8 +106,8 @@ static Key keys[] = { 24 | /* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ 25 | static Button buttons[] = { 26 | /* click event mask button function argument */ 27 | - { ClkLtSymbol, 0, Button1, setlayout, {0} }, 28 | - { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, 29 | + { ClkTagBar, MODKEY, Button1, tag, {0} }, 30 | + { ClkTagBar, MODKEY, Button3, toggletag, {0} }, 31 | { ClkWinTitle, 0, Button2, zoom, {0} }, 32 | { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, 33 | { ClkClientWin, MODKEY, Button1, movemouse, {0} }, 34 | diff --git a/dwm.c b/dwm.c 35 | index a96f33c..941c1c0 100644 36 | --- a/dwm.c 37 | +++ b/dwm.c 38 | @@ -57,12 +57,27 @@ 39 | #define TAGMASK ((1 << LENGTH(tags)) - 1) 40 | #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) 41 | 42 | +#define SYSTEM_TRAY_REQUEST_DOCK 0 43 | +/* XEMBED messages */ 44 | +#define XEMBED_EMBEDDED_NOTIFY 0 45 | +#define XEMBED_WINDOW_ACTIVATE 1 46 | +#define XEMBED_FOCUS_IN 4 47 | +#define XEMBED_MODALITY_ON 10 48 | +#define XEMBED_MAPPED (1 << 0) 49 | +#define XEMBED_WINDOW_ACTIVATE 1 50 | +#define XEMBED_WINDOW_DEACTIVATE 2 51 | +#define VERSION_MAJOR 0 52 | +#define VERSION_MINOR 0 53 | +#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR 54 | + 55 | /* enums */ 56 | enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ 57 | enum { SchemeNorm, SchemeSel }; /* color schemes */ 58 | enum { NetSupported, NetWMName, NetWMState, NetWMCheck, 59 | + NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz, 60 | NetWMFullscreen, NetActiveWindow, NetWMWindowType, 61 | NetWMWindowTypeDialog, NetClientClient, NetLast }; /* EWMH atoms */ 62 | +enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ 63 | enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ 64 | enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, 65 | ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ 66 | @@ -141,6 +156,12 @@ typedef struct { 67 | int monitor; 68 | } Rule; 69 | 70 | +typedef struct Systray Systray; 71 | +struct Systray { 72 | + Window win; 73 | + Client *icons; 74 | +}; 75 | + 76 | /* function declarations */ 77 | static void applyrules(Client *c); 78 | static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); 79 | @@ -172,6 +193,7 @@ static void focusstack(const Arg *arg); 80 | static Atom getatomprop(Client *c, Atom prop); 81 | static int getrootptr(int *x, int *y); 82 | static long getstate(Window w); 83 | +static unsigned int getsystraywidth(); 84 | static int gettextprop(Window w, Atom atom, char *text, unsigned int size); 85 | static void grabbuttons(Client *c, int focused); 86 | static void grabkeys(void); 87 | @@ -189,13 +211,16 @@ static void pop(Client *); 88 | static void propertynotify(XEvent *e); 89 | static void quit(const Arg *arg); 90 | static Monitor *recttomon(int x, int y, int w, int h); 91 | +static void removesystrayicon(Client *i); 92 | static void resize(Client *c, int x, int y, int w, int h, int interact); 93 | +static void resizebarwin(Monitor *m); 94 | static void resizeclient(Client *c, int x, int y, int w, int h); 95 | static void resizemouse(const Arg *arg); 96 | +static void resizerequest(XEvent *e); 97 | static void restack(Monitor *m); 98 | static void run(void); 99 | static void scan(void); 100 | -static int sendevent(Client *c, Atom proto); 101 | +static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4); 102 | static void sendmon(Client *c, Monitor *m); 103 | static void setclientstate(Client *c, long state); 104 | static void setfocus(Client *c); 105 | @@ -207,6 +232,7 @@ static void seturgent(Client *c, int urg); 106 | static void showhide(Client *c); 107 | static void sigchld(int unused); 108 | static void spawn(const Arg *arg); 109 | +static Monitor *systraytomon(Monitor *m); 110 | static void tag(const Arg *arg); 111 | static void tagmon(const Arg *arg); 112 | static void tile(Monitor *); 113 | @@ -224,18 +250,23 @@ static int updategeom(void); 114 | static void updatenumlockmask(void); 115 | static void updatesizehints(Client *c); 116 | static void updatestatus(void); 117 | +static void updatesystray(void); 118 | +static void updatesystrayicongeom(Client *i, int w, int h); 119 | +static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); 120 | static void updatetitle(Client *c); 121 | static void updatewindowtype(Client *c); 122 | static void updatewmhints(Client *c); 123 | static void view(const Arg *arg); 124 | static Client *wintoclient(Window w); 125 | static Monitor *wintomon(Window w); 126 | +static Client *wintosystrayicon(Window w); 127 | static int xerror(Display *dpy, XErrorEvent *ee); 128 | static int xerrordummy(Display *dpy, XErrorEvent *ee); 129 | static int xerrorstart(Display *dpy, XErrorEvent *ee); 130 | static void zoom(const Arg *arg); 131 | 132 | /* variables */ 133 | +static Systray *systray = NULL; 134 | static const char broken[] = "broken"; 135 | static char stext[256]; 136 | static int screen; 137 | @@ -258,9 +289,10 @@ static void (*handler[LASTEvent]) (XEvent *) = { 138 | [MapRequest] = maprequest, 139 | [MotionNotify] = motionnotify, 140 | [PropertyNotify] = propertynotify, 141 | + [ResizeRequest] = resizerequest, 142 | [UnmapNotify] = unmapnotify 143 | }; 144 | -static Atom wmatom[WMLast], netatom[NetLast]; 145 | +static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; 146 | static int running = 1; 147 | static Cur *cursor[CurLast]; 148 | static Clr **scheme; 149 | @@ -440,7 +472,7 @@ buttonpress(XEvent *e) 150 | arg.ui = 1 << i; 151 | } else if (ev->x < x + blw) 152 | click = ClkLtSymbol; 153 | - else if (ev->x > selmon->ww - (int)TEXTW(stext)) 154 | + else if (ev->x > selmon->ww - (int)TEXTW(stext) - getsystraywidth()) 155 | click = ClkStatusText; 156 | else 157 | click = ClkWinTitle; 158 | @@ -483,7 +515,14 @@ cleanup(void) 159 | XUngrabKey(dpy, AnyKey, AnyModifier, root); 160 | while (mons) 161 | cleanupmon(mons); 162 | - for (i = 0; i < CurLast; i++) 163 | + 164 | + if (showsystray) { 165 | + XUnmapWindow(dpy, systray->win); 166 | + XDestroyWindow(dpy, systray->win); 167 | + free(systray); 168 | + } 169 | + 170 | + for (i = 0; i < CurLast; i++) 171 | drw_cur_free(drw, cursor[i]); 172 | for (i = 0; i < LENGTH(colors); i++) 173 | free(scheme[i]); 174 | @@ -513,9 +552,58 @@ cleanupmon(Monitor *mon) 175 | void 176 | clientmessage(XEvent *e) 177 | { 178 | + XWindowAttributes wa; 179 | + XSetWindowAttributes swa; 180 | XClientMessageEvent *cme = &e->xclient; 181 | Client *c = wintoclient(cme->window); 182 | 183 | + if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) { 184 | + /* add systray icons */ 185 | + if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { 186 | + if (!(c = (Client *)calloc(1, sizeof(Client)))) 187 | + die("fatal: could not malloc() %u bytes\n", sizeof(Client)); 188 | + if (!(c->win = cme->data.l[2])) { 189 | + free(c); 190 | + return; 191 | + } 192 | + c->mon = selmon; 193 | + c->next = systray->icons; 194 | + systray->icons = c; 195 | + if (!XGetWindowAttributes(dpy, c->win, &wa)) { 196 | + /* use sane defaults */ 197 | + wa.width = bh; 198 | + wa.height = bh; 199 | + wa.border_width = 0; 200 | + } 201 | + c->x = c->oldx = c->y = c->oldy = 0; 202 | + c->w = c->oldw = wa.width; 203 | + c->h = c->oldh = wa.height; 204 | + c->oldbw = wa.border_width; 205 | + c->bw = 0; 206 | + c->isfloating = True; 207 | + /* reuse tags field as mapped status */ 208 | + c->tags = 1; 209 | + updatesizehints(c); 210 | + updatesystrayicongeom(c, wa.width, wa.height); 211 | + XAddToSaveSet(dpy, c->win); 212 | + XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask); 213 | + XReparentWindow(dpy, c->win, systray->win, 0, 0); 214 | + /* use parents background color */ 215 | + swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; 216 | + XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa); 217 | + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 218 | + /* FIXME not sure if I have to send these events, too */ 219 | + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 220 | + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 221 | + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 222 | + XSync(dpy, False); 223 | + resizebarwin(selmon); 224 | + updatesystray(); 225 | + setclientstate(c, NormalState); 226 | + } 227 | + return; 228 | + } 229 | + 230 | if (!c) 231 | return; 232 | if (cme->message_type == netatom[NetWMState]) { 233 | @@ -568,7 +656,7 @@ configurenotify(XEvent *e) 234 | for (c = m->clients; c; c = c->next) 235 | if (c->isfullscreen) 236 | resizeclient(c, m->mx, m->my, m->mw, m->mh); 237 | - XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); 238 | + resizebarwin(m); 239 | } 240 | focus(NULL); 241 | arrange(NULL); 242 | @@ -653,6 +741,11 @@ destroynotify(XEvent *e) 243 | 244 | if ((c = wintoclient(ev->window))) 245 | unmanage(c, 1); 246 | + else if ((c = wintosystrayicon(ev->window))) { 247 | + removesystrayicon(c); 248 | + resizebarwin(selmon); 249 | + updatesystray(); 250 | + } 251 | } 252 | 253 | void 254 | @@ -696,7 +789,7 @@ dirtomon(int dir) 255 | void 256 | drawbar(Monitor *m) 257 | { 258 | - int x, w, tw = 0; 259 | + int x, w, tw = 0, stw = 0; 260 | int boxs = drw->fonts->h / 9; 261 | int boxw = drw->fonts->h / 6 + 2; 262 | unsigned int i, occ = 0, urg = 0; 263 | @@ -705,13 +798,17 @@ drawbar(Monitor *m) 264 | if (!m->showbar) 265 | return; 266 | 267 | + if(showsystray && m == systraytomon(m) && !systrayonleft) 268 | + stw = getsystraywidth(); 269 | + 270 | /* draw status first so it can be overdrawn by tags later */ 271 | if (m == selmon) { /* status is only drawn on selected monitor */ 272 | drw_setscheme(drw, scheme[SchemeNorm]); 273 | - tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ 274 | - drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); 275 | + tw = TEXTW(stext) - lrpad / 2 + 2; /* 2px extra right padding */ 276 | + drw_text(drw, m->ww - tw - stw, 0, tw, bh, lrpad / 2 - 2, stext, 0); 277 | } 278 | 279 | + resizebarwin(m); 280 | for (c = m->clients; c; c = c->next) { 281 | occ |= c->tags; 282 | if (c->isurgent) 283 | @@ -732,7 +829,7 @@ drawbar(Monitor *m) 284 | drw_setscheme(drw, scheme[SchemeNorm]); 285 | x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); 286 | 287 | - if ((w = m->ww - tw - x) > bh) { 288 | + if ((w = m->ww - tw - stw - x) > bh) { 289 | if (m->sel) { 290 | drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); 291 | drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); 292 | @@ -743,7 +840,7 @@ drawbar(Monitor *m) 293 | drw_rect(drw, x, 0, w, bh, 1, 1); 294 | } 295 | } 296 | - drw_map(drw, m->barwin, 0, 0, m->ww, bh); 297 | + drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh); 298 | } 299 | 300 | void 301 | @@ -780,8 +877,11 @@ expose(XEvent *e) 302 | Monitor *m; 303 | XExposeEvent *ev = &e->xexpose; 304 | 305 | - if (ev->count == 0 && (m = wintomon(ev->window))) 306 | + if (ev->count == 0 && (m = wintomon(ev->window))) { 307 | drawbar(m); 308 | + if (m == selmon) 309 | + updatesystray(); 310 | + } 311 | } 312 | 313 | void 314 | @@ -867,9 +967,17 @@ getatomprop(Client *c, Atom prop) 315 | unsigned char *p = NULL; 316 | Atom da, atom = None; 317 | 318 | - if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, 319 | + /* FIXME getatomprop should return the number of items and a pointer to 320 | + * the stored data instead of this workaround */ 321 | + Atom req = XA_ATOM; 322 | + if (prop == xatom[XembedInfo]) 323 | + req = xatom[XembedInfo]; 324 | + 325 | + if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, 326 | &da, &di, &dl, &dl, &p) == Success && p) { 327 | atom = *(Atom *)p; 328 | + if (da == xatom[XembedInfo] && dl == 2) 329 | + atom = ((Atom *)p)[1]; 330 | XFree(p); 331 | } 332 | return atom; 333 | @@ -903,6 +1011,16 @@ getstate(Window w) 334 | return result; 335 | } 336 | 337 | +unsigned int 338 | +getsystraywidth() 339 | +{ 340 | + unsigned int w = 0; 341 | + Client *i; 342 | + if(showsystray) 343 | + for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ; 344 | + return w ? w + systrayspacing : 1; 345 | +} 346 | + 347 | int 348 | gettextprop(Window w, Atom atom, char *text, unsigned int size) 349 | { 350 | @@ -1007,7 +1125,8 @@ killclient(const Arg *arg) 351 | { 352 | if (!selmon->sel) 353 | return; 354 | - if (!sendevent(selmon->sel, wmatom[WMDelete])) { 355 | + 356 | + if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) { 357 | XGrabServer(dpy); 358 | XSetErrorHandler(xerrordummy); 359 | XSetCloseDownMode(dpy, DestroyAll); 360 | @@ -1096,6 +1215,13 @@ maprequest(XEvent *e) 361 | static XWindowAttributes wa; 362 | XMapRequestEvent *ev = &e->xmaprequest; 363 | 364 | + Client *i; 365 | + if ((i = wintosystrayicon(ev->window))) { 366 | + sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); 367 | + resizebarwin(selmon); 368 | + updatesystray(); 369 | + } 370 | + 371 | if (!XGetWindowAttributes(dpy, ev->window, &wa)) 372 | return; 373 | if (wa.override_redirect) 374 | @@ -1219,7 +1345,18 @@ propertynotify(XEvent *e) 375 | Window trans; 376 | XPropertyEvent *ev = &e->xproperty; 377 | 378 | - if ((ev->window == root) && (ev->atom == XA_WM_NAME)) 379 | + if ((c = wintosystrayicon(ev->window))) { 380 | + if (ev->atom == XA_WM_NORMAL_HINTS) { 381 | + updatesizehints(c); 382 | + updatesystrayicongeom(c, c->w, c->h); 383 | + } 384 | + else 385 | + updatesystrayiconstate(c, ev); 386 | + resizebarwin(selmon); 387 | + updatesystray(); 388 | + } 389 | + 390 | + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) 391 | updatestatus(); 392 | else if (ev->state == PropertyDelete) 393 | return; /* ignore */ 394 | @@ -1269,6 +1406,19 @@ recttomon(int x, int y, int w, int h) 395 | return r; 396 | } 397 | 398 | +void 399 | +removesystrayicon(Client *i) 400 | +{ 401 | + Client **ii; 402 | + 403 | + if (!showsystray || !i) 404 | + return; 405 | + for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next); 406 | + if (ii) 407 | + *ii = i->next; 408 | + free(i); 409 | +} 410 | + 411 | void 412 | resize(Client *c, int x, int y, int w, int h, int interact) 413 | { 414 | @@ -1276,6 +1426,14 @@ resize(Client *c, int x, int y, int w, int h, int interact) 415 | resizeclient(c, x, y, w, h); 416 | } 417 | 418 | +void 419 | +resizebarwin(Monitor *m) { 420 | + unsigned int w = m->ww; 421 | + if (showsystray && m == systraytomon(m) && !systrayonleft) 422 | + w -= getsystraywidth(); 423 | + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh); 424 | +} 425 | + 426 | void 427 | resizeclient(Client *c, int x, int y, int w, int h) 428 | { 429 | @@ -1348,6 +1506,19 @@ resizemouse(const Arg *arg) 430 | } 431 | } 432 | 433 | +void 434 | +resizerequest(XEvent *e) 435 | +{ 436 | + XResizeRequestEvent *ev = &e->xresizerequest; 437 | + Client *i; 438 | + 439 | + if ((i = wintosystrayicon(ev->window))) { 440 | + updatesystrayicongeom(i, ev->width, ev->height); 441 | + resizebarwin(selmon); 442 | + updatesystray(); 443 | + } 444 | +} 445 | + 446 | void 447 | restack(Monitor *m) 448 | { 449 | @@ -1437,26 +1608,37 @@ setclientstate(Client *c, long state) 450 | } 451 | 452 | int 453 | -sendevent(Client *c, Atom proto) 454 | +sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) 455 | { 456 | int n; 457 | - Atom *protocols; 458 | + Atom *protocols, mt; 459 | int exists = 0; 460 | XEvent ev; 461 | 462 | - if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { 463 | - while (!exists && n--) 464 | - exists = protocols[n] == proto; 465 | - XFree(protocols); 466 | + if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { 467 | + mt = wmatom[WMProtocols]; 468 | + if (XGetWMProtocols(dpy, w, &protocols, &n)) { 469 | + while (!exists && n--) 470 | + exists = protocols[n] == proto; 471 | + XFree(protocols); 472 | + } 473 | } 474 | + else { 475 | + exists = True; 476 | + mt = proto; 477 | + } 478 | + 479 | if (exists) { 480 | ev.type = ClientMessage; 481 | - ev.xclient.window = c->win; 482 | - ev.xclient.message_type = wmatom[WMProtocols]; 483 | + ev.xclient.window = w; 484 | + ev.xclient.message_type = mt; 485 | ev.xclient.format = 32; 486 | - ev.xclient.data.l[0] = proto; 487 | - ev.xclient.data.l[1] = CurrentTime; 488 | - XSendEvent(dpy, c->win, False, NoEventMask, &ev); 489 | + ev.xclient.data.l[0] = d0; 490 | + ev.xclient.data.l[1] = d1; 491 | + ev.xclient.data.l[2] = d2; 492 | + ev.xclient.data.l[3] = d3; 493 | + ev.xclient.data.l[4] = d4; 494 | + XSendEvent(dpy, w, False, mask, &ev); 495 | } 496 | return exists; 497 | } 498 | @@ -1470,7 +1652,7 @@ setfocus(Client *c) 499 | XA_WINDOW, 32, PropModeReplace, 500 | (unsigned char *) &(c->win), 1); 501 | } 502 | - sendevent(c, wmatom[WMTakeFocus]); 503 | + sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); 504 | } 505 | 506 | void 507 | @@ -1558,15 +1740,22 @@ setup(void) 508 | wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); 509 | wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); 510 | netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); 511 | - netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); 512 | - netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); 513 | + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); 514 | + netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); 515 | + netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); 516 | + netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); 517 | + netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False); 518 | + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); 519 | netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); 520 | netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); 521 | netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); 522 | netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); 523 | netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); 524 | netatom[NetClientClient] = XInternAtom(dpy, "_NET_CLIENT_Client", False); 525 | - /* init cursors */ 526 | + xatom[Manager] = XInternAtom(dpy, "MANAGER", False); 527 | + xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); 528 | + xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); 529 | + /* init cursors */ 530 | cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); 531 | cursor[CurResize] = drw_cur_create(drw, XC_sizing); 532 | cursor[CurMove] = drw_cur_create(drw, XC_fleur); 533 | @@ -1574,6 +1763,8 @@ setup(void) 534 | scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); 535 | for (i = 0; i < LENGTH(colors); i++) 536 | scheme[i] = drw_scm_create(drw, colors[i], 3); 537 | + /* init system tray */ 538 | + updatesystray(); 539 | /* init bars */ 540 | updatebars(); 541 | updatestatus(); 542 | @@ -1707,7 +1898,18 @@ togglebar(const Arg *arg) 543 | { 544 | selmon->showbar = !selmon->showbar; 545 | updatebarpos(selmon); 546 | - XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); 547 | + resizebarwin(selmon); 548 | + if (showsystray) { 549 | + XWindowChanges wc; 550 | + if (!selmon->showbar) 551 | + wc.y = -bh; 552 | + else if (selmon->showbar) { 553 | + wc.y = 0; 554 | + if (!selmon->topbar) 555 | + wc.y = selmon->mh - bh; 556 | + } 557 | + XConfigureWindow(dpy, systray->win, CWY, &wc); 558 | + } 559 | arrange(selmon); 560 | } 561 | 562 | @@ -1802,11 +2004,18 @@ unmapnotify(XEvent *e) 563 | else 564 | unmanage(c, 0); 565 | } 566 | + else if ((c = wintosystrayicon(ev->window))) { 567 | + /* KLUDGE! sometimes icons occasionally unmap their windows, but do 568 | + * _not_ destroy them. We map those windows back */ 569 | + XMapRaised(dpy, c->win); 570 | + updatesystray(); 571 | + } 572 | } 573 | 574 | void 575 | updatebars(void) 576 | { 577 | + unsigned int w; 578 | Monitor *m; 579 | XSetWindowAttributes wa = { 580 | .override_redirect = True, 581 | @@ -1817,10 +2026,15 @@ updatebars(void) 582 | for (m = mons; m; m = m->next) { 583 | if (m->barwin) 584 | continue; 585 | - m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), 586 | + w = m->ww; 587 | + if (showsystray && m == systraytomon(m)) 588 | + w -= getsystraywidth(); 589 | + m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen), 590 | CopyFromParent, DefaultVisual(dpy, screen), 591 | CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); 592 | XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); 593 | + if (showsystray && m == systraytomon(m)) 594 | + XMapRaised(dpy, systray->win); 595 | XMapRaised(dpy, m->barwin); 596 | XSetClassHint(dpy, m->barwin, &ch); 597 | } 598 | @@ -1996,6 +2210,125 @@ updatestatus(void) 599 | if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) 600 | strcpy(stext, "dwm-"VERSION); 601 | drawbar(selmon); 602 | + updatesystray(); 603 | +} 604 | + 605 | + 606 | +void 607 | +updatesystrayicongeom(Client *i, int w, int h) 608 | +{ 609 | + if (i) { 610 | + i->h = bh; 611 | + if (w == h) 612 | + i->w = bh; 613 | + else if (h == bh) 614 | + i->w = w; 615 | + else 616 | + i->w = (int) ((float)bh * ((float)w / (float)h)); 617 | + applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False); 618 | + /* force icons into the systray dimensions if they don't want to */ 619 | + if (i->h > bh) { 620 | + if (i->w == i->h) 621 | + i->w = bh; 622 | + else 623 | + i->w = (int) ((float)bh * ((float)i->w / (float)i->h)); 624 | + i->h = bh; 625 | + } 626 | + } 627 | +} 628 | + 629 | +void 630 | +updatesystrayiconstate(Client *i, XPropertyEvent *ev) 631 | +{ 632 | + long flags; 633 | + int code = 0; 634 | + 635 | + if (!showsystray || !i || ev->atom != xatom[XembedInfo] || 636 | + !(flags = getatomprop(i, xatom[XembedInfo]))) 637 | + return; 638 | + 639 | + if (flags & XEMBED_MAPPED && !i->tags) { 640 | + i->tags = 1; 641 | + code = XEMBED_WINDOW_ACTIVATE; 642 | + XMapRaised(dpy, i->win); 643 | + setclientstate(i, NormalState); 644 | + } 645 | + else if (!(flags & XEMBED_MAPPED) && i->tags) { 646 | + i->tags = 0; 647 | + code = XEMBED_WINDOW_DEACTIVATE; 648 | + XUnmapWindow(dpy, i->win); 649 | + setclientstate(i, WithdrawnState); 650 | + } 651 | + else 652 | + return; 653 | + sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0, 654 | + systray->win, XEMBED_EMBEDDED_VERSION); 655 | +} 656 | + 657 | +void 658 | +updatesystray(void) 659 | +{ 660 | + XSetWindowAttributes wa; 661 | + XWindowChanges wc; 662 | + Client *i; 663 | + Monitor *m = systraytomon(NULL); 664 | + unsigned int x = m->mx + m->mw; 665 | + unsigned int sw = TEXTW(stext) - lrpad + systrayspacing; 666 | + unsigned int w = 1; 667 | + 668 | + if (!showsystray) 669 | + return; 670 | + if (systrayonleft) 671 | + x -= sw + lrpad / 2; 672 | + if (!systray) { 673 | + /* init systray */ 674 | + if (!(systray = (Systray *)calloc(1, sizeof(Systray)))) 675 | + die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); 676 | + systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel); 677 | + wa.event_mask = ButtonPressMask | ExposureMask; 678 | + wa.override_redirect = True; 679 | + wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; 680 | + XSelectInput(dpy, systray->win, SubstructureNotifyMask); 681 | + XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32, 682 | + PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1); 683 | + XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa); 684 | + XMapRaised(dpy, systray->win); 685 | + XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime); 686 | + if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) { 687 | + sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0); 688 | + XSync(dpy, False); 689 | + } 690 | + else { 691 | + fprintf(stderr, "dwm: unable to obtain system tray.\n"); 692 | + free(systray); 693 | + systray = NULL; 694 | + return; 695 | + } 696 | + } 697 | + for (w = 0, i = systray->icons; i; i = i->next) { 698 | + /* make sure the background color stays the same */ 699 | + wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; 700 | + XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa); 701 | + XMapRaised(dpy, i->win); 702 | + w += systrayspacing; 703 | + i->x = w; 704 | + XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h); 705 | + w += i->w; 706 | + if (i->mon != m) 707 | + i->mon = m; 708 | + } 709 | + w = w ? w + systrayspacing : 1; 710 | + x -= w; 711 | + XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh); 712 | + wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh; 713 | + wc.stack_mode = Above; wc.sibling = m->barwin; 714 | + XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc); 715 | + XMapWindow(dpy, systray->win); 716 | + XMapSubwindows(dpy, systray->win); 717 | + /* redraw background */ 718 | + XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel); 719 | + XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh); 720 | + XSync(dpy, False); 721 | } 722 | 723 | void 724 | @@ -2063,6 +2396,16 @@ wintoclient(Window w) 725 | return NULL; 726 | } 727 | 728 | +Client * 729 | +wintosystrayicon(Window w) { 730 | + Client *i = NULL; 731 | + 732 | + if (!showsystray || !w) 733 | + return i; 734 | + for (i = systray->icons; i && i->win != w; i = i->next) ; 735 | + return i; 736 | +} 737 | + 738 | Monitor * 739 | wintomon(Window w) 740 | { 741 | @@ -2116,6 +2459,22 @@ xerrorstart(Display *dpy, XErrorEvent *ee) 742 | return -1; 743 | } 744 | 745 | +Monitor * 746 | +systraytomon(Monitor *m) { 747 | + Monitor *t; 748 | + int i, n; 749 | + if(!systraypinning) { 750 | + if(!m) 751 | + return selmon; 752 | + return m == selmon ? m : NULL; 753 | + } 754 | + for(n = 1, t = mons; t && t->next; n++, t = t->next) ; 755 | + for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ; 756 | + if(systraypinningfailfirst && n < systraypinning) 757 | + return mons; 758 | + return t; 759 | +} 760 | + 761 | void 762 | zoom(const Arg *arg) 763 | { 764 | -------------------------------------------------------------------------------- /patch/dwm-viewontag-20210312-61bb8b2.diff: -------------------------------------------------------------------------------- 1 | From 03d3c172ff736cb80e12f7bb7cab4e1f250af9dd Mon Sep 17 00:00:00 2001 2 | From: Marco Fleres 3 | Date: Fri, 12 Mar 2021 22:25:53 -0300 4 | Subject: [PATCH] Modification on viewontag patch: windows will not be followed 5 | to the "all" tag 6 | 7 | --- 8 | config.def.h | 1 + 9 | dwm.c | 2 ++ 10 | 2 files changed, 3 insertions(+) 11 | 12 | diff --git a/config.def.h b/config.def.h 13 | index 1c0b587..d7dfb6d 100644 14 | --- a/config.def.h 15 | +++ b/config.def.h 16 | @@ -5,6 +5,7 @@ static const unsigned int borderpx = 1; /* border pixel of windows */ 17 | static const unsigned int snap = 32; /* snap pixel */ 18 | static const int showbar = 1; /* 0 means no bar */ 19 | static const int topbar = 1; /* 0 means bottom bar */ 20 | +static const Bool viewontag = True; /* Switch view on tag switch */ 21 | static const char *fonts[] = { "monospace:size=10" }; 22 | static const char dmenufont[] = "monospace:size=10"; 23 | static const char col_gray1[] = "#222222"; 24 | diff --git a/dwm.c b/dwm.c 25 | index 664c527..8ca2f98 100644 26 | --- a/dwm.c 27 | +++ b/dwm.c 28 | @@ -1660,6 +1660,8 @@ tag(const Arg *arg) 29 | selmon->sel->tags = arg->ui & TAGMASK; 30 | focus(NULL); 31 | arrange(selmon); 32 | + if(viewontag && ((arg->ui & TAGMASK) != TAGMASK)) 33 | + view(arg); 34 | } 35 | } 36 | 37 | -- 38 | 2.30.1 39 | 40 | -------------------------------------------------------------------------------- /statusbar/battery.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import subprocess 6 | import re 7 | import time 8 | import common 9 | import _thread 10 | 11 | 12 | icon_fg=common.green 13 | icon_bg=common.black 14 | icon_tr="0xff" 15 | text_fg=common.green 16 | text_bg=common.black 17 | text_tr="0xff" 18 | 19 | icon_color="^c"+str(icon_fg)+"^^b"+str(icon_bg)+str(icon_tr)+"^" 20 | text_color="^c"+str(text_fg)+"^^b"+str(text_bg)+str(text_tr)+"^" 21 | DELAY_TIME=3 22 | 23 | filename= os.path.basename(__file__) 24 | name=re.sub("\..*",'',filename) 25 | 26 | def get_battery_status(): 27 | charge_icon="" 28 | 29 | cmd="echo $(acpi | sed 's/^Battery 0: //g' | awk -F ',' '{print $1}') " 30 | result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 31 | charge_sta=result.stdout.decode('utf-8').replace('\n','') 32 | 33 | 34 | cmd="echo $(acpi -b | sed 2d | awk '{print $4}' | grep -Eo '[0-9]+')" 35 | result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 36 | battery_text=result.stdout.decode('utf-8').replace('\n','') 37 | 38 | charge_icon="" 39 | bat_icon="" 40 | if (str(charge_sta)=="Discharging") : 41 | bat=int(battery_text) 42 | if(bat>=95): bat_icon=""; 43 | elif(bat>=90): bat_icon=""; 44 | elif(bat>=80): bat_icon=""; 45 | elif(bat>=70): bat_icon=""; 46 | elif(bat>=60): bat_icon=""; 47 | elif(bat>=50): bat_icon=""; 48 | elif(bat>=40): bat_icon=""; 49 | elif(bat>=30): bat_icon=""; 50 | elif(bat>=20): bat_icon=""; 51 | elif(bat>=10): bat_icon=""; 52 | else : bat_icon="󱃍" 53 | elif (str(charge_sta)=="Charging") : 54 | bat=int(battery_text) 55 | if(bat>=95): bat_icon="󰂅"; 56 | elif(bat>=90): bat_icon="󰂋"; 57 | elif(bat>=80): bat_icon="󰂊"; 58 | elif(bat>=70): bat_icon="󰢞"; 59 | elif(bat>=60): bat_icon="󰂉"; 60 | elif(bat>=50): bat_icon="󰢝"; 61 | elif(bat>=40): bat_icon="󰂈"; 62 | elif(bat>=30): bat_icon="󰂇"; 63 | elif(bat>=20): bat_icon="󰂆"; 64 | elif(bat>=10): bat_icon="󰢜"; 65 | else : bat_icon="󰢟" 66 | elif (str(charge_sta)=="Full") : 67 | bat_icon="󰂅"; 68 | else : 69 | bat_icon="󰂑"; 70 | return (bat_icon,battery_text) 71 | 72 | def update(loop=False,exec=True): 73 | while True : 74 | icon="" 75 | text="" 76 | icon,text=get_battery_status() 77 | icon=" "+icon 78 | text=""+text 79 | text+=" " 80 | txt="^s"+str(name)+"^"+str(icon_color)+str(icon)+str(text_color)+str(text) 81 | common.write_to_file(txt+"\n",str(name)) 82 | if loop == False : 83 | if exec==True : 84 | os.system("xsetroot -name '"+str(txt)+"'") 85 | break 86 | time.sleep(DELAY_TIME) 87 | 88 | def update_thread(): 89 | _thread.start_new_thread(update,(False,False)) 90 | 91 | def notify(str='') : 92 | pass 93 | # cmd='notify-send " CPU tops" "$(ps axch -o cmd:15,%cpu --sort=-%cpu | head | '+"sed 's/$/&%/g')"+'"'+" -r 1014" 94 | # os.system(cmd) 95 | 96 | def click(str='') : 97 | match str: 98 | case 'L': 99 | os.system("gnome-power-statistics&") 100 | pass 101 | case 'M': 102 | pass 103 | case 'R': 104 | pass 105 | case 'U': 106 | pass 107 | case 'D': 108 | pass 109 | case _: pass 110 | 111 | 112 | if __name__ == "__main__": 113 | if len(sys.argv) > 1: 114 | if(sys.argv[1]=="update") : 115 | pass 116 | else : 117 | click(sys.argv[1]) 118 | update(exec=False) 119 | else : 120 | update() 121 | -------------------------------------------------------------------------------- /statusbar/bluetooth_battery.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | A python library to get battery level from Bluetooth headsets 5 | """ 6 | 7 | # License: GPL-3.0 8 | # Author: @TheWeirdDev, @GaLaXy102 9 | # 29 Sept 2019 10 | 11 | import argparse 12 | import bluetooth 13 | from typing import Optional, Union, List, Dict 14 | 15 | 16 | class BatteryQueryError(bluetooth.BluetoothError): 17 | pass 18 | 19 | 20 | class SocketDataIterator: 21 | def __init__(self, sock: bluetooth.BluetoothSocket, chunk_size: int = 128): 22 | """ 23 | Create an Iterator over the given Socket 24 | 25 | chunk_size defines the amount of data in Bytes to be read per iteration 26 | """ 27 | self._sock = sock 28 | self._chunk_size = chunk_size 29 | 30 | def __next__(self): 31 | """ 32 | Receive chunks 33 | """ 34 | return self._sock.recv(self._chunk_size) 35 | 36 | 37 | class RFCOMMSocket(bluetooth.BluetoothSocket): 38 | 39 | def __init__(self, proto=bluetooth.RFCOMM, _sock=None): 40 | super().__init__(proto, _sock) 41 | 42 | def __iter__(self): 43 | """ 44 | Iterate over incoming chunks of 128 Bytes 45 | """ 46 | return SocketDataIterator(self) 47 | 48 | @staticmethod 49 | def find_rfcomm_port(device_mac) -> int: 50 | """ 51 | Find the RFCOMM port number for a given bluetooth device 52 | """ 53 | uuid = "0000111e-0000-1000-8000-00805f9b34fb" 54 | services: List[Dict] = bluetooth.find_service(address=device_mac, uuid=uuid) 55 | 56 | for service in services: 57 | if "protocol" in service.keys() and service["protocol"] == "RFCOMM": 58 | return service["port"] 59 | # Raise Interface error when the required service is not offered my the end device 60 | raise bluetooth.BluetoothError("Couldn't find the RFCOMM port number. Perhaps the device is offline?") 61 | 62 | def send(self, data): 63 | """ 64 | This function sends a message through a bluetooth socket with added line separators 65 | """ 66 | return super().send(b"\r\n" + data + b"\r\n") 67 | 68 | 69 | class BatteryStateQuerier: 70 | 71 | def __init__(self, bluetooth_mac: str, bluetooth_port: Optional[Union[str, int]] = None): 72 | """ 73 | Prepare a query for the end devices' battery state 74 | 75 | bluetooth_mac is the MAC of the end device, e.g. 11:22:33:44:55:66 76 | bluetooth_port is the Port of the RFCOMM/SPP service of the end device. 77 | It will be determined automatically if not given. 78 | 79 | The actual query can be performed using the int() and str() method. 80 | """ 81 | self._bt_settings = bluetooth_mac, int(bluetooth_port or RFCOMMSocket.find_rfcomm_port(bluetooth_mac)) 82 | 83 | def __int__(self): 84 | """ 85 | Perform a reading and get the result as int between 0 and 100 86 | """ 87 | return self._perform_query() 88 | 89 | def __str__(self): 90 | """ 91 | Perform a reading and get the result as str between 0% and 100% 92 | """ 93 | return "{:.0%}".format(self._perform_query() / 100) 94 | 95 | def _perform_query(self) -> int: 96 | """ 97 | Will try to get and print the battery level of supported devices 98 | """ 99 | result = None 100 | sock = RFCOMMSocket() 101 | sock.connect(self._bt_settings) 102 | # Iterate received packets until there is no more or a result was found 103 | for line in sock: 104 | if b"BRSF" in line: 105 | sock.send(b"+BRSF: 1024") 106 | sock.send(b"OK") 107 | elif b"CIND=" in line: 108 | sock.send(b"+CIND:(\"service\",(0-1)),(\"call\",(0-1)),(\"callsetup\",(0-3)),(\"callheld\",(0-2)),(\"battchg\",(0-5))") 109 | sock.send(b"OK") 110 | elif b"CIND?" in line: 111 | sock.send(b"+CIND: 0,0,0,0,3") 112 | sock.send(b"OK") 113 | elif b"BIND=?" in line: 114 | # Announce that we support the battery level HF indicator 115 | # https://www.bluetooth.com/specifications/assigned-numbers/hands-free-profile/ 116 | sock.send(b"+BIND: (2)") 117 | sock.send(b"OK") 118 | elif b"BIND?" in line: 119 | # Enable battery level HF indicator 120 | sock.send(b"+BIND: 2,1") 121 | sock.send(b"OK") 122 | elif b"XAPL=" in line: 123 | sock.send(b"+XAPL=iPhone,7") 124 | sock.send(b"OK") 125 | elif b"IPHONEACCEV" in line: 126 | parts = line.strip().split(b',')[1:] 127 | if len(parts) > 1 and (len(parts) % 2) == 0: 128 | parts = iter(parts) 129 | params = dict(zip(parts, parts)) 130 | if b'1' in params: 131 | result = (int(params[b'1']) + 1) * 10 132 | break 133 | elif b"BIEV=" in line: 134 | params = line.strip().split(b"=")[1].split(b",") 135 | if params[0] == b"2": 136 | result = int(params[1]) 137 | break 138 | elif b"XEVENT=BATTERY" in line: 139 | params = line.strip().split(b"=")[1].split(b",") 140 | if len(params) >= 3: 141 | # AT+XEVENT=BATTERY,6,11,461,0 142 | result = int(params[1]) / int(params[2]) * 100 143 | else: 144 | # AT+XEVENT=BATTERY,9 145 | result = (int(params[1]) + 1) * 10 146 | break 147 | else: 148 | sock.send(b"OK") 149 | sock.close() 150 | # Check whether the result was found, otherwise raise an Error 151 | if result is None: 152 | raise BatteryQueryError("Could not query the battery state.") 153 | return result 154 | 155 | 156 | def main(): 157 | """ 158 | The starting point of the program. For each device address in the argument 159 | list a bluetooth socket will be opened and the battery level will be read 160 | and printed to stdout 161 | """ 162 | parser = argparse.ArgumentParser(description="Get battery level from Bluetooth headsets") 163 | parser.add_argument("devices", metavar="DEVICE_MAC[.PORT]", type=str, nargs="+", 164 | help="(MAC address of target)[.SPP Port]") 165 | args = parser.parse_args() 166 | for device in args.devices: 167 | query = BatteryStateQuerier(*device.split(".")) 168 | print("Battery level for {} is {}".format(device, str(query))) 169 | 170 | if __name__ == "__main__": 171 | main() 172 | -------------------------------------------------------------------------------- /statusbar/common.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import fcntl 5 | # import sys 6 | # import subprocess 7 | import re 8 | import threading 9 | # import time 10 | 11 | PACKAGES_LISTS={ 12 | 'music_title':1, 13 | 'music_pre':10, 14 | 'music_play':1, 15 | 'music_next':10, 16 | 'icon':100, 17 | 'screen':3, 18 | # 'pacman':36000, 19 | 'net':1, 20 | 'cpu':2, 21 | 'memory':2, 22 | 'wifi':2, 23 | 'vol':1, 24 | 'battery':3, 25 | 'date':1, 26 | } 27 | 28 | 29 | 30 | DWM_PATH="/home/gxt_kt/my_desktop/dwm/" 31 | PACKAGES_PATH=DWM_PATH+"statusbar/" 32 | TEMP_FILE="/home/gxt_kt/python_tmp" 33 | 34 | MUSIC_PROGRAM="yesplaymusic" 35 | 36 | black="#1e222a" 37 | white="#D8DEE9" 38 | grey="#373d49" 39 | blue="#81A1C1" 40 | blue2="#5E81AC" 41 | blue3="#88C0D0" 42 | blue4="#8FBCBB" 43 | red="#d47d85" 44 | green="#A3BE8C" 45 | pink="#B48EAD" 46 | yellow="#EBCB8B" 47 | orange="#D08770" 48 | darkblue="#7292b2" 49 | 50 | threadLock = threading.Lock() 51 | def write_to_file(string,package_name): 52 | threadLock.acquire() 53 | if (os.path.exists(TEMP_FILE)==False): 54 | os.system("touch "+TEMP_FILE) 55 | with open(TEMP_FILE, 'r+') as f: 56 | lines=f.readlines() 57 | with open(TEMP_FILE, 'w+') as f: 58 | find=False 59 | for line in lines : 60 | if re.match("^\^s",line) == None : 61 | continue 62 | flag=re.match("^\^s"+package_name,line) 63 | if flag==None : 64 | f.write(line) 65 | else : 66 | f.write(string) 67 | find=True 68 | if find==False : 69 | f.write(string) 70 | threadLock.release() 71 | 72 | if __name__ == "__main__": 73 | pass 74 | 75 | -------------------------------------------------------------------------------- /statusbar/cpu.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import subprocess 6 | import re 7 | import time 8 | import common 9 | import _thread 10 | import psutil 11 | 12 | 13 | icon_fg=common.black 14 | icon_bg=common.blue 15 | icon_tr="0xff" 16 | text_fg=common.black 17 | text_bg=common.blue 18 | text_tr="0xff" 19 | 20 | icon_color="^c"+str(icon_fg)+"^^b"+str(icon_bg)+str(icon_tr)+"^" 21 | text_color="^c"+str(text_fg)+"^^b"+str(text_bg)+str(text_tr)+"^" 22 | DELAY_TIME=1 23 | 24 | filename= os.path.basename(__file__) 25 | name=re.sub("\..*",'',filename) 26 | 27 | def update(loop=False,exec=True): 28 | while True : 29 | icon="" 30 | cpu_usage=int(psutil.cpu_percent()) 31 | if(cpu_usage>50) : icon=" " 32 | else : icon=" " 33 | cpu_usage="{:<3}".format(str(cpu_usage)+"%") 34 | cmd = "cat /sys/class/thermal/thermal_zone0/temp" 35 | result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 36 | temperature=int(float(result.stdout.decode('utf-8').replace('\n',''))/1000) 37 | text=cpu_usage+""+str(temperature)+" " 38 | txt="^s"+str(name)+"^"+str(icon_color)+str(icon)+str(text_color)+str(text) 39 | common.write_to_file(txt+"\n",str(name)) 40 | if loop == False : 41 | if exec==True : 42 | os.system("xsetroot -name '"+str(txt)+"'") 43 | break 44 | time.sleep(DELAY_TIME) 45 | 46 | def update_thread(): 47 | _thread.start_new_thread(update,(False,False)) 48 | 49 | def notify(string='') : 50 | cmd='notify-send " CPU tops" "$(ps axch -o cmd:15,%cpu --sort=-%cpu | head | '+"sed 's/$/&%/g')"+'"'+" -r 1014" 51 | os.system(cmd) 52 | 53 | def click(string='') : 54 | match string: 55 | case 'L': 56 | notify() 57 | case 'M': 58 | pass 59 | case 'R': 60 | os.system("alacritty -t statusutil --class floatingTerminal -e btop") 61 | pass 62 | case 'U': 63 | pass 64 | case 'D': 65 | pass 66 | case _: pass 67 | 68 | 69 | if __name__ == "__main__": 70 | if len(sys.argv) > 1: 71 | if(sys.argv[1]=="update") : 72 | pass 73 | else : 74 | click(sys.argv[1]) 75 | update(exec=False) 76 | else : 77 | update() 78 | 79 | -------------------------------------------------------------------------------- /statusbar/date.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from concurrent.futures import thread 4 | import os 5 | import sys 6 | import subprocess 7 | import re 8 | import time 9 | import common 10 | import _thread 11 | 12 | icon_fg=common.black 13 | icon_bg=common.blue 14 | icon_tr="0xff" 15 | text_fg=common.black 16 | text_bg=common.blue 17 | text_tr="0xff" 18 | 19 | icon_color="^c"+str(icon_fg)+"^^b"+str(icon_bg)+str(icon_tr)+"^" 20 | text_color="^c"+str(text_fg)+"^^b"+str(text_bg)+str(text_tr)+"^" 21 | DELAY_TIME=1 22 | 23 | filename= os.path.basename(__file__) 24 | name=re.sub("\..*",'',filename) 25 | 26 | def update(loop=False,exec=True): 27 | while True : 28 | icon="" 29 | text=time.strftime(" %H:%M:%S ", time.localtime()) 30 | txt="^s"+str(name)+"^"+str(icon_color)+str(icon)+str(text_color)+str(text) 31 | common.write_to_file(txt+"\n",str(name)) 32 | if loop == False : 33 | if exec==True : 34 | os.system("xsetroot -name '"+str(txt)+"'") 35 | break 36 | time.sleep(DELAY_TIME) 37 | 38 | def update_thread(): 39 | _thread.start_new_thread(update,(False,False)) 40 | 41 | 42 | def notify(string='') : 43 | cmd = "notify-send "+'"'+" Calendar"+'"'+' "'+"\\nData: $(date '+%y-%m-%d \\nTime: %T')"+'"'+" -r 9540" 44 | os.system(cmd) 45 | pass 46 | 47 | def click(string='') : 48 | match string: 49 | case 'L': 50 | notify() 51 | pass 52 | case 'M': 53 | pass 54 | case 'R': 55 | pass 56 | case 'U': 57 | pass 58 | case 'D': 59 | pass 60 | case _: pass 61 | 62 | if __name__ == "__main__": 63 | if len(sys.argv) > 1: 64 | if(sys.argv[1]=="update") : 65 | pass 66 | else : 67 | click(sys.argv[1]) 68 | update(exec=False) 69 | else : 70 | update() 71 | 72 | -------------------------------------------------------------------------------- /statusbar/icon.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import subprocess 6 | import re 7 | import time 8 | import _thread 9 | import common 10 | 11 | 12 | icon_fg=common.black 13 | icon_bg=common.green 14 | icon_tr="0xff" 15 | text_fg=common.black 16 | text_bg=common.green 17 | text_tr="0xff" 18 | 19 | icon_color="^c"+str(icon_fg)+"^^b"+str(icon_bg)+str(icon_tr)+"^" 20 | text_color="^c"+str(text_fg)+"^^b"+str(text_bg)+str(text_tr)+"^" 21 | DELAY_TIME=10 22 | 23 | filename= os.path.basename(__file__) 24 | name=re.sub("\..*",'',filename) 25 | 26 | 27 | def update(loop=False,exec=True): 28 | while True : 29 | icon="  " 30 | text="" 31 | txt="^s"+str(name)+"^"+str(icon_color)+str(icon)+str(text_color)+str(text) 32 | common.write_to_file(txt+"\n",str(name)) 33 | if loop == False : 34 | if exec==True : 35 | os.system("xsetroot -name '"+str(txt)+"'") 36 | break 37 | time.sleep(DELAY_TIME) 38 | 39 | def update_thread(): 40 | _thread.start_new_thread(update,(False,False)) 41 | 42 | def shutdown(): 43 | os.system("shutdown -h now") 44 | def reboot(): 45 | os.system("reboot") 46 | def sleep(): 47 | os.system("systemctl suspend ") 48 | # os.system("systemctl hibernate ") 49 | def lock(): 50 | os.system("bash "+str(common.DWM_PATH)+"i3lock/lock.sh") 51 | 52 | def system_rofi_set() : 53 | # key:display information value:function 54 | choose={"⏻ Shutdown":"shutdown", 55 | " Reboot":"reboot", 56 | "⏾ Sleep":"sleep", 57 | " Lock":"lock", 58 | } 59 | cmd="echo $(echo -e '" 60 | for choose_string in choose.keys(): 61 | cmd+=choose_string+"\\n" 62 | cmd=cmd[:-2] 63 | cmd+="' | rofi -dmenu -window-title Power)" 64 | print(cmd) 65 | result = subprocess.run(cmd, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 66 | choose_ret=result.stdout.decode('utf-8').replace("\n","") 67 | print(choose_ret) 68 | match_function=choose[choose_ret] 69 | try: 70 | exec(str(match_function)+"()") 71 | except Exception: 72 | pass 73 | 74 | def click(string='') : 75 | match string: 76 | case 'L': 77 | system_rofi_set() 78 | pass 79 | case 'M': 80 | pass 81 | case 'R': 82 | pass 83 | os.system("nitrogen&") 84 | case 'U': 85 | pass 86 | case 'D': 87 | pass 88 | case _: pass 89 | 90 | def notify(string='') : 91 | pass 92 | 93 | if __name__ == "__main__": 94 | if len(sys.argv) > 1: 95 | if(sys.argv[1]=="update") : 96 | pass 97 | else : 98 | click(sys.argv[1]) 99 | update(exec=False) 100 | else : 101 | update() 102 | 103 | -------------------------------------------------------------------------------- /statusbar/memory.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import subprocess 6 | import re 7 | import time 8 | from typing import Tuple 9 | import common 10 | import _thread 11 | 12 | import psutil 13 | 14 | icon_fg=common.black 15 | icon_bg=common.green 16 | icon_tr="0xff" 17 | text_fg=common.black 18 | text_bg=common.green 19 | text_tr="0xff" 20 | 21 | icon_color="^c"+str(icon_fg)+"^^b"+str(icon_bg)+str(icon_tr)+"^" 22 | text_color="^c"+str(text_fg)+"^^b"+str(text_bg)+str(text_tr)+"^" 23 | DELAY_TIME=1 24 | 25 | filename= os.path.basename(__file__) 26 | name=re.sub("\..*",'',filename) 27 | 28 | 29 | def update(loop=False,exec=True): 30 | while True : 31 | icon=" ﬘" 32 | text=str(int(psutil.virtual_memory()[2]))+"% " 33 | txt="^s"+str(name)+"^"+str(icon_color)+str(icon)+str(text_color)+str(text) 34 | common.write_to_file(txt+"\n",str(name)) 35 | if loop == False : 36 | if exec==True : 37 | os.system("xsetroot -name '"+str(txt)+"'") 38 | break 39 | time.sleep(DELAY_TIME) 40 | 41 | def update_thread(): 42 | _thread.start_new_thread(update,(False,False)) 43 | 44 | def click(string='') : 45 | match string: 46 | case 'L': 47 | pass 48 | case 'M': 49 | pass 50 | case 'R': 51 | pass 52 | os.system("alacritty -t statusutil --class floatingTerminal -e btop") 53 | case 'U': 54 | pass 55 | case 'D': 56 | pass 57 | case _: pass 58 | 59 | def notify(string='') : 60 | pass 61 | 62 | if __name__ == "__main__": 63 | if len(sys.argv) > 1: 64 | if(sys.argv[1]=="update") : 65 | pass 66 | else : 67 | click(sys.argv[1]) 68 | update(exec=False) 69 | else : 70 | update() 71 | 72 | -------------------------------------------------------------------------------- /statusbar/music_next.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import subprocess 6 | import re 7 | import time 8 | import _thread 9 | import common 10 | 11 | music_program=common.MUSIC_PROGRAM 12 | 13 | icon_fg=common.black 14 | icon_bg=common.blue 15 | icon_tr="0xff" 16 | text_fg=common.blue 17 | text_bg=common.black 18 | text_tr="0xff" 19 | 20 | icon_color="^c"+str(icon_fg)+"^^b"+str(icon_bg)+str(icon_tr)+"^" 21 | text_color="^c"+str(text_fg)+"^^b"+str(text_bg)+str(text_tr)+"^" 22 | DELAY_TIME=5 23 | 24 | filename= os.path.basename(__file__) 25 | name=re.sub("\..*",'',filename) 26 | 27 | def update(loop=False,exec=True): 28 | while True : 29 | icon="󰒭 " 30 | text="" 31 | txt="^s"+str(name)+"^"+str(icon_color)+str(icon)+str(text_color)+str(text) 32 | common.write_to_file(txt+"\n",str(name)) 33 | if loop == False : 34 | if exec==True : 35 | os.system("xsetroot -name '"+str(txt)+"'") 36 | break 37 | time.sleep(DELAY_TIME) 38 | 39 | def update_thread(): 40 | _thread.start_new_thread(update,(False,False)) 41 | 42 | def click(string='') : 43 | match string: 44 | case 'L': 45 | os.system("playerctl next "+"-p "+str(music_program)) 46 | case 'M': 47 | pass 48 | case 'R': 49 | pass 50 | case 'U': 51 | pass 52 | case 'D': 53 | pass 54 | case _: pass 55 | 56 | def notify(str='') : 57 | pass 58 | 59 | if __name__ == "__main__": 60 | if len(sys.argv) > 1: 61 | if(sys.argv[1]=="update") : 62 | pass 63 | else : 64 | click(sys.argv[1]) 65 | update(exec=False) 66 | else : 67 | update() 68 | 69 | -------------------------------------------------------------------------------- /statusbar/music_play.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import subprocess 6 | import re 7 | import time 8 | import _thread 9 | import common 10 | 11 | music_program=common.MUSIC_PROGRAM 12 | 13 | icon_fg=common.black 14 | icon_bg=common.blue 15 | icon_tr="0xff" 16 | text_fg=common.blue 17 | text_bg=common.black 18 | text_tr="0xff" 19 | 20 | icon_color="^c"+str(icon_fg)+"^^b"+str(icon_bg)+str(icon_tr)+"^" 21 | text_color="^c"+str(text_fg)+"^^b"+str(text_bg)+str(text_tr)+"^" 22 | DELAY_TIME=1 23 | 24 | filename= os.path.basename(__file__) 25 | name=re.sub("\..*",'',filename) 26 | 27 | def geticon(): 28 | icon="" 29 | cmd="echo $(dbus-send --print-reply --dest=org.mpris.MediaPlayer2."+str(music_program)+" /org/mpris/MediaPlayer2 org.freedesktop.DBus.Properties.Get string:org.mpris.MediaPlayer2.Player string:PlaybackStatus | grep -Eo '"+'"'+".*?"+'"'+"'"+ " | cut -d '"+'"'+"'"+" -f 2) 2>/dev/null" 30 | # print(cmd) 31 | result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 32 | play_status=result.stdout.decode('utf-8').replace('\n','') 33 | # print(play_status) 34 | if play_status=="Paused" : icon="  " 35 | elif play_status=="Playing" : icon=" 󰏤 " 36 | else : icon="  " 37 | # print(icon) 38 | return icon 39 | 40 | 41 | 42 | 43 | def update(loop=False,exec=True): 44 | while True : 45 | icon=geticon() 46 | text="" 47 | txt="^s"+str(name)+"^"+str(icon_color)+str(icon)+str(text_color)+str(text) 48 | common.write_to_file(txt+"\n",str(name)) 49 | if loop == False : 50 | if exec==True : 51 | os.system("xsetroot -name '"+str(txt)+"'") 52 | break 53 | time.sleep(DELAY_TIME) 54 | 55 | def update_thread(): 56 | _thread.start_new_thread(update,(False,False)) 57 | 58 | def click(string='') : 59 | match string: 60 | case 'L': 61 | os.system("playerctl play-pause "+"-p "+str(music_program)) 62 | case 'M': 63 | pass 64 | case 'R': 65 | pass 66 | case 'U': 67 | pass 68 | case 'D': 69 | pass 70 | case _: pass 71 | 72 | def notify(string='') : 73 | pass 74 | 75 | if __name__ == "__main__": 76 | if len(sys.argv) > 1: 77 | if(sys.argv[1]=="update") : 78 | pass 79 | else : 80 | click(sys.argv[1]) 81 | update(exec=False) 82 | else : 83 | update() 84 | 85 | -------------------------------------------------------------------------------- /statusbar/music_pre.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import subprocess 6 | import re 7 | import time 8 | import _thread 9 | import common 10 | 11 | music_program=common.MUSIC_PROGRAM 12 | 13 | icon_fg=common.black 14 | icon_bg=common.blue 15 | icon_tr="0xff" 16 | text_fg=common.blue 17 | text_bg=common.black 18 | text_tr="0xff" 19 | 20 | icon_color="^c"+str(icon_fg)+"^^b"+str(icon_bg)+str(icon_tr)+"^" 21 | text_color="^c"+str(text_fg)+"^^b"+str(text_bg)+str(text_tr)+"^" 22 | DELAY_TIME=5 23 | 24 | filename= os.path.basename(__file__) 25 | name=re.sub("\..*",'',filename) 26 | 27 | def update(loop=False,exec=True): 28 | while True : 29 | icon=" 󰒮" 30 | text="" 31 | txt="^s"+str(name)+"^"+str(icon_color)+str(icon)+str(text_color)+str(text) 32 | common.write_to_file(txt+"\n",str(name)) 33 | if loop == False : 34 | if exec==True : 35 | os.system("xsetroot -name '"+str(txt)+"'") 36 | break 37 | time.sleep(DELAY_TIME) 38 | 39 | def update_thread(): 40 | _thread.start_new_thread(update,(False,False)) 41 | 42 | def click(string='') : 43 | match string: 44 | case 'L': 45 | os.system("playerctl previous "+"-p "+str(music_program)) 46 | case 'M': 47 | pass 48 | case 'R': 49 | pass 50 | case 'U': 51 | pass 52 | case 'D': 53 | pass 54 | case _: pass 55 | 56 | def notify(string='') : 57 | pass 58 | 59 | if __name__ == "__main__": 60 | if len(sys.argv) > 1: 61 | if(sys.argv[1]=="update") : 62 | pass 63 | else : 64 | click(sys.argv[1]) 65 | update(exec=False) 66 | else : 67 | update() 68 | 69 | -------------------------------------------------------------------------------- /statusbar/music_title.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import subprocess 6 | import re 7 | import time 8 | import _thread 9 | import common 10 | 11 | music_program=common.MUSIC_PROGRAM 12 | 13 | icon_fg=common.black 14 | icon_bg=common.blue 15 | icon_tr="0xff" 16 | text_fg=common.blue 17 | text_bg=common.black 18 | text_tr="0xff" 19 | 20 | icon_color="^c"+str(icon_fg)+"^^b"+str(icon_bg)+str(icon_tr)+"^" 21 | text_color="^c"+str(text_fg)+"^^b"+str(text_bg)+str(text_tr)+"^" 22 | DELAY_TIME=1 23 | 24 | filename= os.path.basename(__file__) 25 | name=re.sub("\..*",'',filename) 26 | 27 | def get_music_title(): 28 | cmd="echo $(dbus-send --print-reply --dest=org.mpris.MediaPlayer2."+str(music_program)+" /org/mpris/MediaPlayer2 org.freedesktop.DBus.Properties.Get string:org.mpris.MediaPlayer2.Player string:Metadata | sed -n '/title/{n;p}' "+"| cut -d '"+'"'+"' -f 2) 2>/dev/null" 29 | result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 30 | title=result.stdout.decode('utf-8').replace('\n','') 31 | title=title.replace("'","") # 解决一些歌曲带'的问题 32 | title=" "+title+" " 33 | return (title) 34 | 35 | def update(loop=False,exec=True): 36 | while True : 37 | icon="🎵" # 󰎆 38 | text=get_music_title() 39 | txt="^s"+str(name)+"^"+str(icon_color)+str(icon)+str(text_color)+str(text) 40 | common.write_to_file(txt+"\n",str(name)) 41 | if loop == False : 42 | if exec==True : 43 | os.system("xsetroot -name '"+str(txt)+"'") 44 | break 45 | time.sleep(DELAY_TIME) 46 | 47 | def update_thread(): 48 | _thread.start_new_thread(update,(False,False)) 49 | 50 | def click(string='') : 51 | match string: 52 | case 'L': 53 | os.system("xdotool keydown Super m keyup m Super") 54 | case 'M': 55 | pass 56 | case 'R': 57 | pass 58 | case 'U': 59 | pass 60 | case 'D': 61 | pass 62 | case _: pass 63 | 64 | def notify(string='') : 65 | pass 66 | 67 | if __name__ == "__main__": 68 | if len(sys.argv) > 1: 69 | if(sys.argv[1]=="update") : 70 | pass 71 | else : 72 | click(sys.argv[1]) 73 | update(exec=False) 74 | else : 75 | update() 76 | 77 | -------------------------------------------------------------------------------- /statusbar/net.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import subprocess 6 | import re 7 | import time 8 | from typing import Tuple 9 | import _thread 10 | import common 11 | 12 | 13 | icon_fg=common.blue 14 | icon_bg=common.black 15 | icon_tr="0xff" 16 | text_fg=common.blue 17 | text_bg=common.black 18 | text_tr="0xff" 19 | 20 | icon_color="^c"+str(icon_fg)+"^^b"+str(icon_bg)+str(icon_tr)+"^" 21 | text_color="^c"+str(text_fg)+"^^b"+str(text_bg)+str(text_tr)+"^" 22 | 23 | DELAY_TIME=1 24 | 25 | filename= os.path.basename(__file__) 26 | name=re.sub("\..*",'',filename) 27 | 28 | 29 | def get_speed(val:int)->str: 30 | ret="0" 31 | if(val<1024) : 32 | ret="{:^8}".format(str(val)+"B") 33 | elif val<1048576 : 34 | ret="{:^8}".format("{:.1f}".format(val/1024)+"KB") 35 | else : 36 | ret="{:^8}".format("{:.1f}".format(val/1048576)+"MB") 37 | return ret 38 | 39 | def getnet()->Tuple[str,str]: 40 | rx_bytes_cur=0 41 | cmd="cat /sys/class/net/[ew]*/statistics/rx_bytes" 42 | result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 43 | rx_bytes_string=result.stdout.decode('utf-8') 44 | for rx in rx_bytes_string.splitlines(): 45 | rx_bytes_cur+=int(rx) 46 | TX_POSITON="~/.cache/rx_bytes" 47 | if (os.path.exists(TX_POSITON)==False): 48 | os.system("touch "+TX_POSITON) 49 | cmd="cat "+TX_POSITON 50 | result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 51 | rx_bytes_pre=result.stdout.decode('utf-8').replace("\n","") 52 | rx_bytes=0 53 | if rx_bytes_pre!="": 54 | rx_bytes=abs(int(rx_bytes_cur)-int(rx_bytes_pre)) 55 | # write new rx_bytes_cur 56 | cmd="echo "+str(rx_bytes_cur)+" > "+TX_POSITON 57 | os.system(cmd) 58 | 59 | tx_bytes_cur=0 60 | cmd="cat /sys/class/net/[ew]*/statistics/tx_bytes" 61 | result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 62 | tx_bytes_string=result.stdout.decode('utf-8') 63 | for tx in tx_bytes_string.splitlines(): 64 | tx_bytes_cur+=int(tx) 65 | TX_POSITON="~/.cache/tx_bytes" 66 | if (os.path.exists(TX_POSITON)==False): 67 | os.system("touch "+TX_POSITON) 68 | cmd="cat "+TX_POSITON 69 | result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 70 | tx_bytes_pre=result.stdout.decode('utf-8').replace("\n","") 71 | tx_bytes=0 72 | if tx_bytes_pre!="": 73 | tx_bytes=abs(int(tx_bytes_cur)-int(tx_bytes_pre)) 74 | # write new tx_bytes_cur 75 | cmd="echo "+str(tx_bytes_cur)+" > "+TX_POSITON 76 | os.system(cmd) 77 | 78 | send_string=str(get_speed(tx_bytes)) 79 | recv_string=str(get_speed(rx_bytes)) 80 | # print(send_string) 81 | # print(recv_string) 82 | return (" "+send_string,""+recv_string) 83 | 84 | def update(loop=False,exec=True): 85 | while True : 86 | icon="" 87 | text="" 88 | for string in getnet(): 89 | text+=string 90 | txt="^s"+str(name)+"^"+str(icon_color)+str(icon)+str(text_color)+str(text) 91 | common.write_to_file(txt+"\n",str(name)) 92 | if loop == False : 93 | if exec==True : 94 | os.system("xsetroot -name '"+str(txt)+"'") 95 | break 96 | time.sleep(DELAY_TIME) 97 | 98 | def update_thread(): 99 | _thread.start_new_thread(update,(False,False)) 100 | 101 | def click(string='') : 102 | match string: 103 | case 'L': 104 | pass 105 | case 'M': 106 | pass 107 | case 'R': 108 | pass 109 | case 'U': 110 | pass 111 | case 'D': 112 | pass 113 | case _: pass 114 | 115 | def notify(string='') : 116 | pass 117 | 118 | if __name__ == "__main__": 119 | if len(sys.argv) > 1: 120 | if(sys.argv[1]=="update") : 121 | pass 122 | else : 123 | click(sys.argv[1]) 124 | update(exec=False) 125 | else : 126 | update() 127 | 128 | -------------------------------------------------------------------------------- /statusbar/pacman.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import subprocess 6 | import re 7 | import time 8 | import _thread 9 | import common 10 | 11 | 12 | icon_fg=common.black 13 | icon_bg=common.green 14 | icon_tr="0xff" 15 | text_fg=common.black 16 | text_bg=common.green 17 | text_tr="0xff" 18 | 19 | icon_color="^c"+str(icon_fg)+"^^b"+str(icon_bg)+str(icon_tr)+"^" 20 | text_color="^c"+str(text_fg)+"^^b"+str(text_bg)+str(text_tr)+"^" 21 | DELAY_TIME=18000 22 | 23 | filename= os.path.basename(__file__) 24 | name=re.sub("\..*",'',filename) 25 | 26 | def get_update_packages_nums(): 27 | num=0 28 | os.system("notify-send 'Get update info... ... ...' -r 1011") 29 | os.system("sudo pacman -Sy") 30 | os.system("notify-send 'Get Update info completed !' -r 1011") 31 | cmd="echo $(pacman -Qu | grep -Fcv '[ignored]' )" 32 | result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 33 | num=int(result.stdout.decode('utf-8').replace('\n','')) 34 | os.system("notify-send 'Totally "+str(num)+" packages can update' -r 1011") 35 | return str(num) 36 | 37 | def update_packages(): 38 | num=get_update_packages_nums() 39 | os.system("notify-send 'Preparing update ... ... ...' -r 1012") 40 | os.system("sudo pacman -Su") 41 | os.system("notify-send 'Update completed' -r 1012") 42 | 43 | 44 | 45 | def update(loop=False,exec=True): 46 | while True : 47 | icon=" " 48 | text=str(get_update_packages_nums())+" " 49 | txt="^s"+str(name)+"^"+str(icon_color)+str(icon)+str(text_color)+str(text) 50 | common.write_to_file(txt+"\n",str(name)) 51 | if loop == False : 52 | if exec==True : 53 | os.system("xsetroot -name '"+str(txt)+"'") 54 | break 55 | time.sleep(DELAY_TIME) 56 | 57 | def update_thread(): 58 | _thread.start_new_thread(update,(False,False)) 59 | 60 | def notify(string='') : 61 | pass 62 | 63 | def click(string='') : 64 | match string: 65 | case 'L': 66 | notify() 67 | case 'M': 68 | pass 69 | case 'R': 70 | update_packages() 71 | pass 72 | case 'U': 73 | pass 74 | case 'D': 75 | pass 76 | case _: pass 77 | 78 | 79 | if __name__ == "__main__": 80 | if len(sys.argv) > 1: 81 | if(sys.argv[1]=="update") : 82 | pass 83 | else : 84 | update(exec=False) 85 | click(sys.argv[1]) 86 | update(exec=False) 87 | else : 88 | update() 89 | 90 | -------------------------------------------------------------------------------- /statusbar/screen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import subprocess 6 | import re 7 | import time 8 | import _thread 9 | import common 10 | import screeninfo 11 | 12 | 13 | icon_fg=common.black 14 | icon_bg=common.green 15 | icon_tr="0xff" 16 | text_fg=common.black 17 | text_bg=common.green 18 | text_tr="0xff" 19 | 20 | icon_color="^c"+str(icon_fg)+"^^b"+str(icon_bg)+str(icon_tr)+"^" 21 | text_color="^c"+str(text_fg)+"^^b"+str(text_bg)+str(text_tr)+"^" 22 | DELAY_TIME=3 23 | 24 | filename= os.path.basename(__file__) 25 | name=re.sub("\..*",'',filename) 26 | 27 | def update(loop=False,exec=True): 28 | while True : 29 | icon=" 󰹑" 30 | 31 | # TODO: 32 | # 暂时有个问题是执行和xrandr相关的命令会卡顿,暂时没找到解决方法 33 | # 如果有人知道怎么用python得到得到连接显示器数量,欢迎提issue或pr,谢谢. 34 | connected_ports=0 35 | 36 | # cmd="autorandr --fingerprint" 37 | # result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 38 | # port_string=result.stdout.decode('utf-8') # note that no replace('\n','') 39 | # connected_ports=int(port_string.count('\n')) 40 | 41 | # cmd="xrandr | grep -Eo '\\bconnected\\b'" 42 | # p = subprocess.Popen(args=cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True) 43 | # tmp=p.communicate() 44 | # connected_ports=str(tmp).count('connected') 45 | 46 | # cmd="xrandr | grep -Eo '\\bconnected\\b'" 47 | # result=subprocess.Popen(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 48 | # connected_string=result.stdout.decode('utf-8').replace('\n','') 49 | # connected_ports=connected_string.count('connected') 50 | 51 | # cmd="echo $(xrandr | grep -w 'connected' | awk '{print $1}' | wc -l)" 52 | # result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 53 | # connected_ports=result.stdout.decode('utf-8').replace('\n','') 54 | 55 | connected_monitors=0 56 | screen_info=str(screeninfo.get_monitors()) 57 | connected_monitors=screen_info.count('Monitor') 58 | # cmd="echo $(xrandr --listmonitors | sed 1d | awk '{print $4}' | wc -l)" 59 | # result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 60 | # connected_monitors=result.stdout.decode('utf-8').replace('\n','') 61 | 62 | # TODO: 63 | # text=" "+str(connected_monitors)+"/"+str(connected_ports)+" " 64 | text=" "+str(connected_monitors)+" " 65 | txt="^s"+str(name)+"^"+str(icon_color)+str(icon)+str(text_color)+str(text) 66 | common.write_to_file(txt+"\n",str(name)) 67 | if loop == False : 68 | if exec==True : 69 | os.system("xsetroot -name '"+str(txt)+"'") 70 | break 71 | time.sleep(DELAY_TIME) 72 | 73 | def update_thread(): 74 | _thread.start_new_thread(update,(False,False)) 75 | 76 | def get_all_screen_status() : 77 | eDP="" 78 | HDMI="" 79 | DP="" 80 | 81 | cmd='xrandr | rg "\\beDP.*? .*? " -o' 82 | result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 83 | eDP=result.stdout.decode('utf-8') 84 | 85 | cmd='xrandr | rg "\\bHDMI.*? .*? " -o' 86 | result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 87 | HDMI=result.stdout.decode('utf-8') 88 | 89 | cmd='xrandr | rg "\\bDP.*? .*? " -o' 90 | result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 91 | DP=result.stdout.decode('utf-8') 92 | 93 | return (eDP,HDMI,DP) 94 | 95 | def Set_Auto(): 96 | os.system("autorandr --change") 97 | pass 98 | def Set_4k_L____2k_P_R_2_0(): 99 | os.system("\ 100 | xrandr --output DP-0 --off \ 101 | --output DP-1 --off \ 102 | --output DP-2 --off \ 103 | --output DP-3 --off \ 104 | --output HDMI-0 --primary --mode 2560x1440 --rate 120 --scale 2.0x2.0 --pos 3840x0 --rotate normal \ 105 | --output eDP-1-0 --mode 3840x2160 --rate 60 --dpi 192 --pos 0x0 --rotate normal \ 106 | ") 107 | pass 108 | def Set_4k_L____2k_P_R_1_75(): 109 | os.system("\ 110 | xrandr --output DP-0 --off \ 111 | --output DP-1 --off \ 112 | --output DP-2 --off \ 113 | --output DP-3 --off \ 114 | --output HDMI-0 --primary --mode 2560x1440 --rate 120 --scale 1.75x1.75 --pos 3840x0 --rotate normal \ 115 | --output eDP-1-0 --mode 3840x2160 --rate 60 --dpi 192 --pos 0x0 --rotate normal \ 116 | ") 117 | pass 118 | def Set_4k_L____2k_P_R_1_5(): 119 | os.system("\ 120 | xrandr --output DP-0 --off \ 121 | --output DP-1 --off \ 122 | --output DP-2 --off \ 123 | --output DP-3 --off \ 124 | --output HDMI-0 --primary --mode 2560x1440 --rate 120 --scale 1.5x1.5 --pos 3840x0 --rotate normal \ 125 | --output eDP-1-0 --mode 3840x2160 --rate 60 --dpi 192 --pos 0x0 --rotate normal \ 126 | ") 127 | pass 128 | def Set_4k_S___1k_S_2_0(): 129 | os.system("\ 130 | xrandr --output DP-0 --off \ 131 | --output DP-1 --off \ 132 | --output DP-2 --off \ 133 | --output DP-3 --off \ 134 | --output HDMI-0 --primary --mode 1920x1080 --rate 60 --scale 2x2 --pos 0x0 --rotate normal \ 135 | --output eDP-1-0 --mode 3840x2160 --rate 60 --dpi 192 --pos 0x0 --rotate normal \ 136 | ") 137 | pass 138 | def Set_4k_Single(): 139 | os.system("\ 140 | xrandr --output DP-0 --off \ 141 | --output DP-1 --off \ 142 | --output DP-2 --off \ 143 | --output DP-3 --off \ 144 | --output HDMI-0 --off \ 145 | --output eDP-1-0 --mode 3840x2160 --rate 60 --dpi 192 \ 146 | ") 147 | pass 148 | 149 | def screen_rofi_set() : 150 | # key:display information value:function 151 | choose={ 152 | "Auto":"Set_Auto", 153 | "4k(L)+2k(P)(R)(2.0)":"Set_4k_L____2k_P_R_2_0", 154 | "4k(L)+2k(P)(R)(1.75)":"Set_4k_L____2k_P_R_1_75", 155 | "4k(L)+2k(P)(R)(1.5)":"Set_4k_L____2k_P_R_1_5", 156 | "4k(S)+1k(S)(2)":"Set_4k_S___1k_S_2_0", 157 | "4k_Single":"Set_4k_Single", 158 | } 159 | cmd="echo $(echo -e '" 160 | for choose_string in choose.keys(): 161 | cmd+=choose_string+"\\n" 162 | cmd=cmd[:-2] 163 | cmd+="' | rofi -dmenu -window-title Screen)" 164 | print(cmd) 165 | result = subprocess.run(cmd, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 166 | choose_ret=result.stdout.decode('utf-8').replace("\n","") 167 | print(choose_ret) 168 | match_function=choose[choose_ret] 169 | try: 170 | exec(str(match_function)+"()") 171 | except Exception: 172 | pass 173 | 174 | def notify(string='') : 175 | send_string="" 176 | for string_ in get_all_screen_status(): 177 | send_string+=string_ 178 | os.system("notify-send "+" '󰹑 Screen Info' "+"'"+send_string+"' -r 1212") 179 | pass 180 | 181 | def click(string='') : 182 | match string: 183 | case 'L': 184 | screen_rofi_set() 185 | case 'M': 186 | pass 187 | case 'R': 188 | notify() 189 | pass 190 | case 'U': 191 | pass 192 | case 'D': 193 | pass 194 | case _: pass 195 | 196 | 197 | if __name__ == "__main__": 198 | if len(sys.argv) > 1: 199 | if(sys.argv[1]=="update") : 200 | pass 201 | else : 202 | click(sys.argv[1]) 203 | update(exec=False) 204 | else : 205 | update() 206 | -------------------------------------------------------------------------------- /statusbar/statusbar.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import fcntl 6 | import subprocess 7 | import _thread 8 | import time 9 | import re 10 | import common 11 | import threading 12 | # from apscheduler.schedulers.blocking import BlockingScheduler 13 | from apscheduler.schedulers.background import BackgroundScheduler 14 | 15 | packages_list=common.PACKAGES_LISTS 16 | 17 | # import packages 18 | for name in packages_list.keys(): 19 | exec('import ' + str(name)) 20 | 21 | 22 | def ExecOtherFile(): 23 | cmd='python3 '+ common.PACKAGES_PATH + str(sys.argv[1]) + '.py ' 24 | for string in sys.argv[2:]: 25 | cmd=cmd+string+' ' 26 | result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 27 | 28 | 29 | def MainRefresh(): 30 | tmp="" 31 | lines="" 32 | 33 | common.threadLock.acquire() 34 | if (os.path.exists(common.TEMP_FILE)==False): 35 | os.system("touch "+common.TEMP_FILE) 36 | with open(common.TEMP_FILE, 'r+') as f: 37 | lines=f.readlines() 38 | common.threadLock.release() 39 | 40 | packages=packages_list.keys() 41 | for name in packages: 42 | match_string="^\^s"+str(name) 43 | for line in lines : 44 | flag=re.match(str(match_string),line) 45 | if flag!=None : 46 | exec(str(name)+"_txt"+"=line.encode('utf-8').decode('utf-8').replace('\\n','')") 47 | tmp+=locals()[str(name)+"_txt"] 48 | break 49 | os.system("xsetroot -name '"+str(tmp)+"'") 50 | 51 | 52 | def Run() : 53 | # add new thread 54 | # for name in packages_list: 55 | # exec("_thread.start_new_thread("+str(name)+".update,(True,))") 56 | 57 | # scheduler = BlockingScheduler() 58 | scheduler = BackgroundScheduler() 59 | for key,value in packages_list.items(): 60 | cmd="scheduler.add_job("+str(key)+".update_thread, 'interval', seconds="+str(int(value))+", id='"+str(key)+"')" 61 | exec(cmd) 62 | # scheduler.add_job(MainRefresh, 'interval', seconds=1, id='MainRefresh') 63 | scheduler.start() 64 | 65 | while True : 66 | # print("debug point 1") 67 | MainRefresh() 68 | time.sleep(0.5) 69 | 70 | 71 | 72 | if __name__ == "__main__": 73 | if len(sys.argv) > 1: 74 | if(sys.argv[1]=="cron") : 75 | Run() 76 | pass 77 | elif(sys.argv[1]=="update") : 78 | pass 79 | else : 80 | # for string in sys.argv : 81 | # print(string) 82 | # # cmd="echo '" +str(string) + "'" + ">> python_debug" 83 | # cmd="echo '" +str(string) + "'" 84 | # # cmd = "echo '123' >> python_debug" 85 | # result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 86 | 87 | ExecOtherFile() 88 | # Run() 89 | 90 | -------------------------------------------------------------------------------- /statusbar/vol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import subprocess 6 | import re 7 | import time 8 | import _thread 9 | import common 10 | 11 | 12 | icon_fg=common.black 13 | icon_bg=common.blue 14 | icon_tr="0xff" 15 | text_fg=common.black 16 | text_bg=common.blue 17 | text_tr="0xff" 18 | 19 | icon_color="^c"+str(icon_fg)+"^^b"+str(icon_bg)+str(icon_tr)+"^" 20 | text_color="^c"+str(text_fg)+"^^b"+str(text_bg)+str(text_tr)+"^" 21 | DELAY_TIME=1 22 | 23 | filename= os.path.basename(__file__) 24 | name=re.sub("\..*",'',filename) 25 | 26 | vol_text="--" 27 | vol_icon="ﱝ" 28 | volumuted="" 29 | 30 | # ref: https://github.com/TheWeirdDev/Bluetooth_Headset_Battery_Level 31 | # Bug need Install : pip3 install git+https://github.com/pybluez/pybluez@master 32 | def GetBluetoothBattery(): 33 | # from bluetooth_battery import BatteryStateQuerier, BatteryQueryError, BluetoothError 34 | from bluetooth_battery import BatteryStateQuerier, BatteryQueryError 35 | from bluetooth import BluetoothError 36 | try: 37 | # Autodetects SPP port 38 | query = BatteryStateQuerier("94:37:F7:73:DB:03") 39 | # # or with given port 40 | # query = BatteryStateQuerier("11:22:33:44:55:66", "4") 41 | # result = int(query) # returns integer between 0 and 100 42 | # result = str(query) # returns "0%".."100%" 43 | icon="󰥊" 44 | result =int(query) 45 | if(result>=95) : icon="󰥈" 46 | elif(result>=90) : icon="󰥆" 47 | elif(result>=80) : icon="󰥅" 48 | elif(result>=70) : icon="󰥄" 49 | elif(result>=60) : icon="󰥃" 50 | elif(result>=50) : icon="󰥂" 51 | elif(result>=40) : icon="󰥁" 52 | elif(result>=30) : icon="󰥀" 53 | elif(result>=20) : icon="󰤿" 54 | elif(result>=10) : icon="󰤾" 55 | else : icon="󰥇" 56 | 57 | # return str("󰥰"+result) 58 | return str(icon) 59 | # Can raise BatteryQueryError when the device is unsupported 60 | except BluetoothError as e: 61 | # Handle device is offline 62 | return "󱔑" 63 | except BatteryQueryError as e: 64 | # Handle device is unsupported 65 | return "󱃓" 66 | 67 | 68 | def GetBluetoothBatteryByPactl(): 69 | icon="󰥊" 70 | # cmd = 'pactl list cards | grep -E "bluetooth\.battery" ' 71 | cmd = 'pactl list cards | grep -E "bluetooth\.battery" | grep -E "[0-9]*%" | grep -Eo "[0-9]*" ' 72 | result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 73 | battery=result.stdout.decode('utf-8').replace('\n','') 74 | if battery!="" : 75 | result =int(battery) 76 | if(result>=95) : icon="󰥈" 77 | elif(result>=90) : icon="󰥆" 78 | elif(result>=80) : icon="󰥅" 79 | elif(result>=70) : icon="󰥄" 80 | elif(result>=60) : icon="󰥃" 81 | elif(result>=50) : icon="󰥂" 82 | elif(result>=40) : icon="󰥁" 83 | elif(result>=30) : icon="󰥀" 84 | elif(result>=20) : icon="󰤿" 85 | elif(result>=10) : icon="󰤾" 86 | else : icon="󰥇" 87 | else : 88 | icon="󱔑" 89 | return icon 90 | 91 | 92 | 93 | def get_vol_content(): 94 | global vol_text 95 | global vol_icon 96 | global volumuted 97 | 98 | cmd="echo $(pactl info | grep 'Default Sink' | awk '{print $3}')" 99 | result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 100 | sink=str(result.stdout.decode('utf-8').replace('\n','')) 101 | 102 | cmd="echo $(pactl list sinks | grep "+str(sink)+" -A 6 | sed -n '7p' | grep 'Mute: no')" 103 | result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 104 | volumuted=str(result.stdout.decode('utf-8').replace('\n','')) 105 | 106 | cmd="echo $(pactl list sinks | grep "+str(sink)+" -A 7 | sed -n '8p' | awk '{printf int($5)}' )" 107 | result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 108 | vol_text=str(result.stdout.decode('utf-8').replace('\n','')) 109 | 110 | if volumuted=="" : 111 | vol_text="--" 112 | vol_icon="ﱝ" 113 | else : 114 | vol=int(vol_text) 115 | vol_text=vol_text 116 | if vol==0 : 117 | vol_icon="婢" 118 | vol_text="00" 119 | # elif vol<10 : vol_icon="奔" 120 | # elif vol<50 : vol_icon="奔" 121 | else : vol_icon="墳" 122 | return str(vol_icon)+str(vol_text)+"%"+" "+GetBluetoothBatteryByPactl() 123 | # return str(vol_icon)+str(vol_text)+"%"+" "+GetBluetoothBattery() 124 | # return str(vol_icon)+str(vol_text)+"%" 125 | 126 | def update(loop=False,exec=True): 127 | while True : 128 | icon="" 129 | icon=" "+get_vol_content()+" " 130 | text="" 131 | txt="^s"+str(name)+"^"+str(icon_color)+str(icon)+str(text_color)+str(text) 132 | common.write_to_file(txt+"\n",str(name)) 133 | if loop == False : 134 | if exec==True : 135 | os.system("xsetroot -name '"+str(txt)+"'") 136 | break 137 | time.sleep(DELAY_TIME) 138 | 139 | def update_thread(): 140 | _thread.start_new_thread(update,(False,False)) 141 | 142 | def notify(string='') : 143 | global vol_text 144 | global vol_icon 145 | global volumuted 146 | get_vol_content(); 147 | 148 | cmd="" 149 | if volumuted=="" : 150 | cmd="notify-send -r 9527 '婢 mute' " 151 | else : 152 | cmd="notify-send -r 9527 -h int:value:"+str(int(vol_text))+" -h string:hlcolor:#dddddd "+'"'+str(vol_icon)+" Volume"+'"' ; 153 | os.system(cmd) 154 | pass 155 | 156 | def click(string='') : 157 | match string: 158 | case 'L': 159 | notify() 160 | pass 161 | case 'M': 162 | os.system("pactl set-sink-mute @DEFAULT_SINK@ toggle") 163 | notify() 164 | pass 165 | case 'R': 166 | os.system("killall pavucontrol || pavucontrol --class floatingTerminal &") 167 | pass 168 | case 'U': 169 | pass 170 | os.system("pactl set-sink-volume @DEFAULT_SINK@ +5%; notify") 171 | notify() 172 | case 'D': 173 | os.system("pactl set-sink-volume @DEFAULT_SINK@ -5%; notify") 174 | notify() 175 | pass 176 | case _: pass 177 | 178 | 179 | if __name__ == "__main__": 180 | if len(sys.argv) > 1: 181 | if(sys.argv[1]=="update") : 182 | pass 183 | elif(sys.argv[1]=="notify") : 184 | notify() 185 | else : 186 | update(exec=False) 187 | click(sys.argv[1]) 188 | update(exec=False) 189 | else : 190 | update() 191 | -------------------------------------------------------------------------------- /statusbar/wifi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import subprocess 6 | import re 7 | import time 8 | import common 9 | import _thread 10 | 11 | 12 | icon_fg=common.black 13 | icon_bg=common.green 14 | icon_tr="0xff" 15 | text_fg=common.black 16 | text_bg=common.green 17 | text_tr="0xff" 18 | 19 | icon_color="^c"+str(icon_fg)+"^^b"+str(icon_bg)+str(icon_tr)+"^" 20 | text_color="^c"+str(text_fg)+"^^b"+str(text_bg)+str(text_tr)+"^" 21 | DELAY_TIME=2 22 | 23 | filename= os.path.basename(__file__) 24 | name=re.sub("\..*",'',filename) 25 | 26 | def connect_status(): 27 | ret=-1 28 | cmd ="cat /sys/class/net/w*/operstate" 29 | result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 30 | connect=str(result.stdout.decode('utf-8').replace('\n','')) 31 | match connect: 32 | case "up": ret=1; 33 | case "down": ret=0; 34 | case _: ret=-1; 35 | return ret 36 | 37 | 38 | def get_wifi_icon(): 39 | icon="󱛏" 40 | connect_status_=connect_status() 41 | match connect_status_: 42 | case 0: 43 | cmd ="cat /sys/class/net/w*/flags" 44 | result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 45 | flags=str(result.stdout.decode('utf-8').replace('\n','')) 46 | if(str(flags)=="0x1003") : icon="睊" 47 | else : icon = "󰤬" 48 | pass 49 | case 1: 50 | cmd = "echo $(awk '/^\s*w/ { print int($3 * 100 / 70)}' /proc/net/wireless)" 51 | result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 52 | wifi_signal=int(result.stdout.decode('utf-8').replace('\n','')) 53 | if(wifi_signal>=80) : icon="󰤨" 54 | elif(wifi_signal>=60) : icon="󰤥" 55 | elif(wifi_signal>=40) : icon="󰤢" 56 | elif(wifi_signal>=20) : icon="󰤟" 57 | else : icon="󰤯" 58 | case _: icon="󱛏" 59 | return icon 60 | 61 | 62 | def update(loop=False,exec=True): 63 | while True : 64 | icon="󱛏" 65 | icon=""+get_wifi_icon()+" " 66 | text="" 67 | txt="^s"+str(name)+"^"+str(icon_color)+str(icon)+str(text_color)+str(text) 68 | common.write_to_file(txt+"\n",str(name)) 69 | if loop == False : 70 | if exec==True : 71 | os.system("xsetroot -name '"+str(txt)+"'") 72 | break 73 | time.sleep(DELAY_TIME) 74 | 75 | def update_thread(): 76 | _thread.start_new_thread(update,(False,False)) 77 | 78 | def notify(string='') : 79 | connect_status_=connect_status() 80 | match int(connect_status_): 81 | case 1: 82 | cmd="echo $(awk '/^\s*w/ { print int($3 * 100 / 70)}' /proc/net/wireless)" 83 | result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 84 | wifi_signal=int(result.stdout.decode('utf-8').replace('\n','')) 85 | cmd="echo $(nmcli -t -f name,device connection show --active | grep wlan0 | cut -d\: -f1)" 86 | result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 87 | wifi_name=result.stdout.decode('utf-8').replace('\n','') 88 | cmd="notify-send 'Wifi connected' "+"'Wifi name : "+str(wifi_name)+"\nSignal strength : "+str(wifi_signal)+"'"+" -r 1025" 89 | os.system(cmd) 90 | case -1: 91 | os.system("notify-send 'Wifi no connected' 'Press right buttom to open wifi connect tool.(nmtui)' -r 1024") 92 | pass 93 | case _: 94 | os.system("notify-send 'The wifi device is disable, please cheack your wifi device' 'Press right buttom to open wifi connect tool.(nmtui)' -r 1024") 95 | pass 96 | 97 | 98 | 99 | def click(string='') : 100 | match string: 101 | case 'L': 102 | notify() 103 | case 'M': 104 | os.system("nm-connection-editor") 105 | pass 106 | case 'R': 107 | os.system("alacritty -t nmtui --class floatingTerminal -e nmtui ") 108 | case 'U': 109 | pass 110 | case 'D': 111 | pass 112 | case _: pass 113 | 114 | 115 | if __name__ == "__main__": 116 | if len(sys.argv) > 1: 117 | if(sys.argv[1]=="update") : 118 | pass 119 | else : 120 | click(sys.argv[1]) 121 | update(exec=False) 122 | else : 123 | update() 124 | 125 | -------------------------------------------------------------------------------- /themes/catppuccin.h: -------------------------------------------------------------------------------- 1 | static const char black[] = "#1E1D2D"; 2 | static const char gray2[] = "#282737"; // unfocused window border 3 | static const char gray3[] = "#585767"; 4 | static const char gray4[] = "#282737"; 5 | static const char blue[] = "#96CDFB"; // focused window border 6 | static const char green[] = "#ABE9B3"; 7 | static const char red[] = "#F28FAD"; 8 | static const char orange[] = "#F8BD96"; 9 | static const char yellow[] = "#FAE3B0"; 10 | static const char pink[] = "#d5aeea"; 11 | static const char col_borderbar[] = "#1E1D2D"; // inner border 12 | static const char white[] = "#f8f8f2"; -------------------------------------------------------------------------------- /themes/dracula.h: -------------------------------------------------------------------------------- 1 | static const char black[] = "#21222C"; 2 | static const char white[] = "#f8f8f2"; 3 | static const char gray2[] = "#282a36"; // unfocused window border 4 | static const char gray3[] = "#44475a"; 5 | static const char gray4[] = "#282a36"; 6 | static const char blue[] = "#bd93f9"; // focused window border 7 | static const char green[] = "#50fa7b"; 8 | static const char red[] = "#ff5555"; 9 | static const char orange[] = "#ffb86c"; 10 | static const char yellow[] = "#f1fa8c"; 11 | static const char pink[] = "#ff79c6"; 12 | static const char col_borderbar[] = "#21222c"; // inner border 13 | -------------------------------------------------------------------------------- /themes/gruvchad.h: -------------------------------------------------------------------------------- 1 | static const char black[] = "#1e2122"; 2 | static const char white[] = "#c7b89d"; 3 | static const char gray2[] = "#282b2c"; // unfocused window border 4 | static const char gray3[] = "#5d6061"; 5 | static const char gray4[] = "#282b2c"; 6 | static const char blue[] = "#6f8faf"; // focused window border 7 | static const char green[] = "#89b482"; 8 | static const char red[] = "#ec6b64"; 9 | static const char orange[] = "#d6b676"; 10 | static const char yellow[] = "#d1b171"; 11 | static const char pink[] = "#cc7f94"; 12 | static const char col_borderbar[] = "#1e2122"; // inner border 13 | -------------------------------------------------------------------------------- /themes/nord.h: -------------------------------------------------------------------------------- 1 | static const char black[] = "#2A303C"; 2 | static const char white[] = "#D8DEE9"; 3 | static const char gray2[] = "#3B4252"; // unfocused window border 4 | static const char gray3[] = "#606672"; 5 | static const char gray4[] = "#6d8dad"; 6 | static const char blue[] = "#81A1C1"; // focused window border 7 | static const char green[] = "#89b482"; 8 | static const char red1[] = "#d57780"; 9 | static const char red2[] = "#f578a8"; 10 | static const char orange[] = "#caaa6a"; 11 | static const char yellow[] = "#EBCB8B"; 12 | static const char pink[] = "#e39a83"; 13 | static const char col_borderbar[] = "#2A303C"; // inner border 14 | -------------------------------------------------------------------------------- /themes/onedark.h: -------------------------------------------------------------------------------- 1 | static const char black[] = "#1e222a"; 2 | static const char white[] = "#abb2bf"; 3 | static const char gray2[] = "#2e323a"; // unfocused window border 4 | static const char gray3[] = "#545862"; 5 | static const char gray4[] = "#6d8dad"; 6 | static const char blue[] = "#61afef"; // focused window border 7 | static const char green[] = "#7EC7A2"; 8 | static const char red[] = "#e06c75"; 9 | static const char orange[] = "#caaa6a"; 10 | static const char yellow[] = "#EBCB8B"; 11 | static const char pink[] = "#c678dd"; 12 | static const char col_borderbar[] = "#1e222a"; // inner border 13 | -------------------------------------------------------------------------------- /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.cpp: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "util.h" 8 | 9 | void * 10 | ecalloc(size_t nmemb, size_t size) 11 | { 12 | void *p; 13 | 14 | if (!(p = calloc(nmemb, size))) 15 | die("calloc:"); 16 | return p; 17 | } 18 | 19 | void 20 | die(const char *fmt, ...) { 21 | va_list ap; 22 | 23 | va_start(ap, fmt); 24 | vfprintf(stderr, fmt, ap); 25 | va_end(ap); 26 | 27 | if (fmt[0] && fmt[strlen(fmt)-1] == ':') { 28 | fputc(' ', stderr); 29 | perror(NULL); 30 | } else { 31 | fputc('\n', stderr); 32 | } 33 | 34 | exit(1); 35 | } 36 | -------------------------------------------------------------------------------- /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 *fmt, ...); 8 | void *ecalloc(size_t nmemb, size_t size); 9 | --------------------------------------------------------------------------------