├── dwm.png ├── .gitignore ├── util.h ├── DEF ├── set_vol.sh ├── blurlock.sh ├── statusbar │ ├── packages │ │ ├── music.sh │ │ ├── icons.sh │ │ ├── wifi.sh │ │ ├── mem.sh │ │ ├── cpu.sh │ │ ├── vol.sh │ │ ├── date.sh │ │ └── bat.sh │ └── statusbar.sh ├── rofi.sh ├── autostart.sh └── config.h ├── util.c ├── transient.c ├── config.mk ├── flake.lock ├── flake.nix ├── Makefile ├── LICENSE ├── drw.h ├── README.md ├── dwm.1 ├── drw.c └── dwm.c /dwm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yaocccc/dwm/HEAD/dwm.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | config.h 2 | autostart.sh 3 | statusbar/ 4 | 5 | result/ 6 | dwm 7 | *.o 8 | .ccls-cache/ 9 | compile_commands.json 10 | .cache 11 | statusbar/temp 12 | DEF/statusbar/temp 13 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /DEF/set_vol.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | dwmdir=$(cd $(dirname $0); cd .. ;pwd) 4 | 5 | sink=$(pactl info | grep 'Default Sink' | awk '{print $3}') 6 | vol=$(pactl list sinks | grep $sink -A 7 | sed -n '8p' | awk '{printf int($5)}') 7 | mod=$((vol % 5)) 8 | 9 | case $1 in 10 | up) target="+$((5 - mod))%" ;; 11 | down) [ $mod -eq 0 ] && target="-5%" || target="-$mod%" ;; 12 | esac 13 | 14 | pactl set-sink-volume @DEFAULT_SINK@ $target 15 | bash $dwmdir/statusbar/statusbar.sh update vol 16 | bash $dwmdir/statusbar/packages/vol.sh notify 17 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /DEF/blurlock.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 依赖包: i3lock-color 3 | 4 | i3lock \ 5 | --blur 5 \ 6 | --bar-indicator \ 7 | --bar-pos y+h \ 8 | --bar-direction 1 \ 9 | --bar-max-height 50 \ 10 | --bar-base-width 50 \ 11 | --bar-color 00000022 \ 12 | --keyhl-color ffffffcc \ 13 | --bar-periodic-step 50 \ 14 | --bar-step 20 \ 15 | --redraw-thread \ 16 | --clock \ 17 | --force-clock \ 18 | --time-pos x+5:y+h-80 xdotool\ 19 | --time-color ffffffff \ 20 | --date-pos tx:ty+15 \ 21 | --date-color ffffffff \ 22 | --date-align 1 \ 23 | --time-align 1 \ 24 | --ringver-color ffffff00 \ 25 | --ringwrong-color ffffff88 \ 26 | --status-pos x+5:y+h-16 \ 27 | --verif-align 1 \ 28 | --wrong-align 1 \ 29 | --verif-color ffffffff \ 30 | --wrong-color ffffffff \ 31 | --modif-pos -50:-50 32 | xdotool mousemove_relative 1 1 # 该命令用于解决自动锁屏后未展示锁屏界面的问题(移动一下鼠标) 33 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | #CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} 30 | CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -O2 ${INCS} ${CPPFLAGS} 31 | LDFLAGS = ${LIBS} 32 | 33 | # Solaris 34 | #CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" 35 | #LDFLAGS = ${LIBS} 36 | 37 | # compiler and linker 38 | CC = cc 39 | -------------------------------------------------------------------------------- /DEF/statusbar/packages/music.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # music 脚本 3 | 4 | tempfile=$(cd $(dirname $0);cd ..;pwd)/temp 5 | 6 | this=_music 7 | icon_color="^c#3B102B^^b#6873790x88^" 8 | text_color="^c#3B102B^^b#6873790x99^" 9 | signal=$(echo "^s$this^" | sed 's/_//') 10 | 11 | [ ! "$(command -v mpc)" ] && echo command not found: mpc && return 12 | 13 | update() { 14 | music_text="$(mpc current)" 15 | icon=" 󰝚 " 16 | if $music_text=~"\""; then 17 | text=$(echo $music_text | sed -e "s/\"\\\\\"/g") 18 | else 19 | text=" $music_text " 20 | fi 21 | [ "$(mpc status | grep "paused")" ] && icon=" 󰐎 " 22 | 23 | sed -i '/^export '$this'=.*$/d' $tempfile 24 | [ ! "$music_text" ] && return 25 | printf "export %s=\"%s%s%s%s%s\"\n" $this "$signal" "$icon_color" "$icon" "$text_color" "$text" >> $tempfile 26 | } 27 | 28 | click() { 29 | case "$1" in 30 | L) mpc toggle ;; 31 | R) mpc toggle ;; 32 | U) mpc prev ;; 33 | D) mpc next ;; 34 | esac 35 | } 36 | 37 | case "$1" in 38 | click) click $2 ;; 39 | notify) notify ;; 40 | *) update ;; 41 | esac 42 | -------------------------------------------------------------------------------- /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": 1675763311, 21 | "narHash": "sha256-bz0Q2H3mxsF1CUfk26Sl9Uzi8/HFjGFD/moZHz1HebU=", 22 | "owner": "NixOS", 23 | "repo": "nixpkgs", 24 | "rev": "fab09085df1b60d6a0870c8a89ce26d5a4a708c2", 25 | "type": "github" 26 | }, 27 | "original": { 28 | "id": "nixpkgs", 29 | "ref": "nixos-unstable", 30 | "type": "indirect" 31 | } 32 | }, 33 | "root": { 34 | "inputs": { 35 | "flake-utils": "flake-utils", 36 | "nixpkgs": "nixpkgs" 37 | } 38 | } 39 | }, 40 | "root": "root", 41 | "version": 7 42 | } 43 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "A very basic flake"; 3 | inputs = { 4 | nixpkgs.url = "nixpkgs/nixos-unstable"; 5 | flake-utils.url = "github:numtide/flake-utils"; 6 | }; 7 | 8 | outputs = 9 | { self 10 | , nixpkgs 11 | , flake-utils 12 | , 13 | }: 14 | let 15 | overlay = 16 | final: prev: { 17 | dwm = prev.dwm.overrideAttrs (oldAttrs: rec { 18 | postPatch = (oldAttrs.postPatch or "") + '' 19 | cp -r DEF/* . 20 | ''; 21 | version = "master"; 22 | src = ./.; 23 | }); 24 | }; 25 | in 26 | flake-utils.lib.eachDefaultSystem 27 | ( 28 | system: 29 | let 30 | pkgs = import nixpkgs { 31 | inherit system; 32 | overlays = [ 33 | self.overlays.default 34 | ]; 35 | }; 36 | in 37 | rec { 38 | packages.dwm = pkgs.dwm; 39 | packages.default = pkgs.dwm; 40 | devShells.default = pkgs.mkShell { 41 | buildInputs = with pkgs; [ xorg.libX11 xorg.libXft xorg.libXinerama gcc ]; 42 | }; 43 | } 44 | ) 45 | // { overlays.default = overlay; }; 46 | } 47 | -------------------------------------------------------------------------------- /DEF/rofi.sh: -------------------------------------------------------------------------------- 1 | # 打印菜单 2 | call_menu() { 3 | echo ' set wallpaper' 4 | echo '艹 update statusbar' 5 | [ "$(ps aux | grep -v grep | grep daed)" ] && echo ' close daed' || echo ' open daed' 6 | [ "$(ps aux | grep picom | grep -v 'grep\|rofi\|nvim')" ] && echo ' close picom' || echo ' open picom' 7 | } 8 | 9 | # 执行菜单 10 | execute_menu() { 11 | case $1 in 12 | ' set wallpaper') 13 | feh --randomize --bg-fill ~/Pictures/wallpaper/*.png 14 | ;; 15 | '艹 update statusbar') 16 | coproc ($DWM/statusbar/statusbar.sh updateall > /dev/null 2>&1) 17 | ;; 18 | ' open daed') 19 | coproc (sudo systemctl start daed > /dev/null && $DWM/statusbar/statusbar.sh updateall > /dev/null) 20 | ;; 21 | ' close daed') 22 | coproc (sudo systemctl stop daed > /dev/null && $DWM/statusbar/statusbar.sh updateall > /dev/null) 23 | ;; 24 | ' open picom') 25 | coproc (picom --experimental-backends --config ~/scripts/config/picom.conf > /dev/null 2>&1) 26 | ;; 27 | ' close picom') 28 | killall picom 29 | ;; 30 | esac 31 | } 32 | 33 | execute_menu "$(call_menu | rofi -dmenu -p "")" 34 | -------------------------------------------------------------------------------- /DEF/autostart.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # DWM自启动脚本 仅作参考 3 | # 搭配 https://github.com/yaocccc/scripts 仓库使用 目录位置 ~/scripts 4 | # 部分配置文件在 ~/scripts/config 目录下 5 | 6 | _thisdir=$(cd $(dirname $0);pwd) 7 | 8 | settings() { 9 | [ $1 ] && sleep $1 10 | xset -b # 关闭蜂鸣器 11 | syndaemon -i 1 -t -K -R -d # 设置使用键盘时触控板短暂失效 12 | ~/scripts/set_screen.sh two # 设置显示器 13 | } 14 | 15 | daemons() { 16 | [ $1 ] && sleep $1 17 | $_thisdir/statusbar/statusbar.sh cron & # 开启状态栏定时更新 18 | xss-lock -- ~/scripts/blurlock.sh & # 开启自动锁屏程序 19 | fcitx5 & # 开启输入法 20 | lemonade server & # 开启lemonade 远程剪切板支持 21 | flameshot & # 截图要跑一个程序在后台 不然无法将截图保存到剪贴板 22 | dunst -conf ~/scripts/config/dunst.conf & # 开启通知server 23 | picom --experimental-backends --config ~/scripts/config/picom.conf >> /dev/null 2>&1 & # 开启picom 24 | } 25 | 26 | cron() { 27 | [ $1 ] && sleep $1 28 | let i=10 29 | while true; do 30 | [ $((i % 10)) -eq 0 ] && ~/scripts/set_screen.sh check # 每10秒检查显示器状态 以此自动设置显示器 31 | [ $((i % 300)) -eq 0 ] && feh --randomize --bg-fill ~/Pictures/wallpaper/*.png # 每300秒更新壁纸 32 | sleep 10; let i+=10 33 | done 34 | } 35 | 36 | settings 1 & # 初始化设置项 37 | daemons 3 & # 后台程序项 38 | cron 5 & # 定时任务项 39 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # dwm - dynamic window manager 2 | # See LICENSE file for copyright and license details. 3 | 4 | include config.mk 5 | 6 | SRC = drw.c dwm.c util.c 7 | OBJ = ${SRC:.c=.o} 8 | 9 | all: check options dwm 10 | 11 | check: 12 | @ [ -f "config.h" ] || echo -e "\033[31mconfig.h not found, please run 'cp DEF/config.h .'\033[0m" 13 | @ [ -f "autostart.sh" ] || echo -e "\033[31mautostart.sh not found, please run 'cp DEF/autostart.sh .'\033[0m" 14 | @ [ -d "statusbar" ] || echo -e "\033[31mstatusbar/ not found, please run 'cp -r DEF/statusbar .'\033[0m" 15 | @ ([ -f "config.h" ] && [ -f "autostart.sh" ] && [ -d "statusbar" ]) || exit 1 16 | 17 | options: 18 | @echo dwm build options: 19 | @echo "CFLAGS = ${CFLAGS}" 20 | @echo "LDFLAGS = ${LDFLAGS}" 21 | @echo "CC = ${CC}" 22 | 23 | .c.o: 24 | ${CC} -c ${CFLAGS} $< 25 | 26 | ${OBJ}: config.h config.mk 27 | 28 | dwm: ${OBJ} 29 | ${CC} -o $@ ${OBJ} ${LDFLAGS} 30 | 31 | clean: 32 | rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz 33 | 34 | install: all 35 | mkdir -p ${DESTDIR}${PREFIX}/bin 36 | cp -f dwm ${DESTDIR}${PREFIX}/bin 37 | chmod 755 ${DESTDIR}${PREFIX}/bin/dwm 38 | mkdir -p ${DESTDIR}${MANPREFIX}/man1 39 | sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1 40 | chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1 41 | rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz 42 | 43 | uninstall: 44 | rm -f ${DESTDIR}${PREFIX}/bin/dwm\ 45 | ${DESTDIR}${MANPREFIX}/man1/dwm.1 46 | 47 | .PHONY: all check options clean install uninstall 48 | -------------------------------------------------------------------------------- /DEF/statusbar/packages/icons.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # ICONS 部分特殊的标记图标 这里是我自己用的,你用不上的话去掉就行 3 | 4 | tempfile=$(cd $(dirname $0);cd ..;pwd)/temp 5 | 6 | this=_icons 7 | color="^c#2D1B46^^b#5555660x66^" 8 | signal=$(echo "^s$this^" | sed 's/_//') 9 | 10 | with_daed() { 11 | [ "$(ps aux | grep -v grep | grep 'daed')" ] && icons=(${icons[@]} "") 12 | } 13 | 14 | with_bluetooth() { 15 | # 此处为自用蓝牙设备的 MAC 地址,你可以自定义该部分 16 | [ ! "$(command -v bluetoothctl)" ] && echo command not found: bluetoothctl && return 17 | [ "$(bluetoothctl info 88:C9:E8:14:2A:72 | grep 'Connected: yes')" ] && icons=(${icons[@]} "") 18 | } 19 | 20 | update() { 21 | icons=("") 22 | with_daed 23 | # with_bluetooth 24 | 25 | text=" ${icons[@]} " 26 | 27 | sed -i '/^export '$this'=.*$/d' $tempfile 28 | printf "export %s='%s%s%s'\n" $this "$signal" "$color" "$text" >> $tempfile 29 | } 30 | 31 | notify() { 32 | texts="" 33 | [ "$(ps aux | grep -v grep | grep 'v2raya')" ] && texts="$texts\n v2raya 已启动" 34 | [ "$(bluetoothctl info 88:C9:E8:14:2A:72 | grep 'Connected: yes')" ] && texts="$texts\n WH-1000XM4 已链接" 35 | [ "$texts" != "" ] && notify-send " Info" "$texts" -r 9527 36 | } 37 | 38 | call_menu() { 39 | case $(echo -e ' 关机\n 重启\n 休眠\n 锁定' | rofi -dmenu -window-title power) in 40 | " 关机") poweroff ;; 41 | " 重启") reboot ;; 42 | " 休眠") systemctl hibernate ;; 43 | " 锁定") ~/scripts/blurlock.sh ;; 44 | esac 45 | } 46 | 47 | click() { 48 | case "$1" in 49 | L) notify; feh --randomize --bg-fill ~/Pictures/wallpaper/*.png ;; 50 | R) call_menu ;; 51 | esac 52 | } 53 | 54 | case "$1" in 55 | click) click $2 ;; 56 | notify) notify ;; 57 | *) update ;; 58 | esac 59 | -------------------------------------------------------------------------------- /DEF/statusbar/packages/wifi.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | tempfile=$(cd $(dirname $0);cd ..;pwd)/temp 4 | 5 | this=_wifi 6 | icon_color="^c#000080^^b#3870560x88^" 7 | text_color="^c#000080^^b#3870560x99^" 8 | signal=$(echo "^s$this^" | sed 's/_//') 9 | 10 | # check 11 | [ ! "$(command -v nmcli)" ] && echo command not found: nmcli && exit 12 | 13 | # 中英文适配 14 | wifi_grep_keyword="已连接 到" 15 | wifi_disconnected="未连接" 16 | wifi_disconnected_notify="未连接到网络" 17 | if [ "$LANG" != "zh_CN.UTF-8" ]; then 18 | wifi_grep_keyword="connected to" 19 | wifi_disconnected="disconnected" 20 | wifi_disconnected_notify="disconnected" 21 | fi 22 | 23 | update() { 24 | wifi_icon="褐" 25 | wifi_text=$(nmcli | grep "$wifi_grep_keyword" | awk -F "$wifi_grep_keyword" '{print $2}') 26 | [ "$wifi_text" = "" ] && wifi_text=$wifi_disconnected 27 | 28 | icon=" $wifi_icon " 29 | text="$wifi_text " 30 | 31 | sed -i '/^export '$this'=.*$/d' $tempfile 32 | printf "export %s='%s%s%s%s%s'\n" $this "$signal" "$icon_color" "$icon" "$text_color" "$text" >> $tempfile 33 | } 34 | 35 | notify() { 36 | update 37 | notify-send -r 9527 "$wifi_icon Wifi" "\n$wifi_text" 38 | } 39 | 40 | call_nm() { 41 | pid1=`ps aux | grep 'st -t statusutil' | grep -v grep | awk '{print $2}'` 42 | pid2=`ps aux | grep 'st -t statusutil_nm' | grep -v grep | awk '{print $2}'` 43 | mx=`xdotool getmouselocation --shell | grep X= | sed 's/X=//'` 44 | my=`xdotool getmouselocation --shell | grep Y= | sed 's/Y=//'` 45 | kill $pid1 && kill $pid2 || st -t statusutil_nm -g 60x25+$((mx - 240))+$((my + 20)) -c FGN -C "#222D31@4" -e 'nmtui-connect' 46 | } 47 | 48 | click() { 49 | case "$1" in 50 | L) notify ;; 51 | R) setsid call_nm ;; 52 | esac 53 | } 54 | 55 | case "$1" in 56 | click) click $2 ;; 57 | notify) notify ;; 58 | *) update ;; 59 | esac 60 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /DEF/statusbar/packages/mem.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # MEM 3 | 4 | tempfile=$(cd $(dirname $0);cd ..;pwd)/temp 5 | 6 | this=_mem 7 | icon_color="^c#3B001B^^b#6873790x88^" 8 | text_color="^c#3B001B^^b#6873790x99^" 9 | signal=$(echo "^s$this^" | sed 's/_//') 10 | 11 | update() { 12 | mem_icon="" 13 | mem_total=$(cat /proc/meminfo | grep "MemTotal:"| awk '{print $2}') 14 | mem_free=$(cat /proc/meminfo | grep "MemFree:"| awk '{print $2}') 15 | mem_buffers=$(cat /proc/meminfo | grep "Buffers:"| awk '{print $2}') 16 | mem_cached=$(cat /proc/meminfo | grep -w "Cached:"| awk '{print $2}') 17 | men_usage_rate=$(((mem_total - mem_free - mem_buffers - mem_cached) * 100 / mem_total)) 18 | mem_text=$(echo $men_usage_rate | awk '{printf "%02d%", $1}') 19 | 20 | icon=" $mem_icon " 21 | text=" $mem_text " 22 | 23 | sed -i '/^export '$this'=.*$/d' $tempfile 24 | printf "export %s='%s%s%s%s%s'\n" $this "$signal" "$icon_color" "$icon" "$text_color" "$text" >> $tempfile 25 | } 26 | 27 | notify() { 28 | free_result=`free -h` 29 | text=" 30 | 可用:\t $(echo "$free_result" | sed -n 2p | awk '{print $7}') 31 | 用量:\t $(echo "$free_result" | sed -n 2p | awk '{print $3}')/$(echo "$free_result" | sed -n 2p | awk '{print $2}') 32 | swap:\t $(echo "$free_result" | sed -n 3p | awk '{print $3}')/$(echo "$free_result" | sed -n 3p | awk '{print $2}') 33 | " 34 | notify-send " Memory" "$text" -r 9527 35 | } 36 | 37 | call_btop() { 38 | pid1=`ps aux | grep 'st -t statusutil' | grep -v grep | awk '{print $2}'` 39 | pid2=`ps aux | grep 'st -t statusutil_mem' | grep -v grep | awk '{print $2}'` 40 | mx=`xdotool getmouselocation --shell | grep X= | sed 's/X=//'` 41 | my=`xdotool getmouselocation --shell | grep Y= | sed 's/Y=//'` 42 | kill $pid1 && kill $pid2 || st -t statusutil_mem -g 82x25+$((mx - 328))+$((my + 20)) -c FGN -e btop 43 | } 44 | 45 | click() { 46 | case "$1" in 47 | L) notify ;; 48 | R) call_btop ;; 49 | esac 50 | } 51 | 52 | case "$1" in 53 | click) click $2 ;; 54 | notify) notify ;; 55 | *) update ;; 56 | esac 57 | -------------------------------------------------------------------------------- /DEF/statusbar/packages/cpu.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # CPU 获取CPU使用率和温度的脚本 3 | 4 | tempfile=$(cd $(dirname $0);cd ..;pwd)/temp 5 | 6 | this=_cpu 7 | icon_color="^c#3E206F^^b#6E51760x88^" 8 | text_color="^c#3E206F^^b#6E51760x99^" 9 | signal=$(echo "^s$this^" | sed 's/_//') 10 | 11 | with_temp() { 12 | # check 13 | [ ! "$(command -v sensors)" ] && echo command not found: sensors && return 14 | 15 | temp_text=$(sensors | grep Tctl | awk '{printf "%d°C", $2}') 16 | text=" $cpu_text $temp_text " 17 | } 18 | 19 | update() { 20 | cpu_icon="閭" 21 | cpu_text=$(top -n 1 -b | sed -n '3p' | awk '{printf "%02d%", 100 - $8}') 22 | 23 | icon=" $cpu_icon " 24 | text=" $cpu_text " 25 | 26 | with_temp 27 | 28 | sed -i '/^export '$this'=.*$/d' $tempfile 29 | printf "export %s='%s%s%s%s%s'\n" $this "$signal" "$icon_color" "$icon" "$text_color" "$text" >> $tempfile 30 | } 31 | 32 | notify() { 33 | tops=$( 34 | ps axch -o cmd:15,%cpu --sort=-%cpu | awk ' 35 | { 36 | cmd = gensub(/.*\//, "", "g", $1); 37 | cpu[cmd] += $2; 38 | } 39 | END { 40 | printf "%-15s %s\n", "COMMAND", "CPU (%)"; 41 | for (cmd in cpu) { 42 | printf "%-15s %.2f\n", cmd, cpu[cmd]; 43 | } 44 | }' | sort -k2 -nr | head 45 | ) 46 | notify-send "󰘚 CPU tops" "\n$tops\\n\\n(100% per core)" -r 9527 47 | } 48 | 49 | call_btop() { 50 | pid1=`ps aux | grep 'st -t statusutil' | grep -v grep | awk '{print $2}'` 51 | pid2=`ps aux | grep 'st -t statusutil_cpu' | grep -v grep | awk '{print $2}'` 52 | mx=`xdotool getmouselocation --shell | grep X= | sed 's/X=//'` 53 | my=`xdotool getmouselocation --shell | grep Y= | sed 's/Y=//'` 54 | kill $pid1 && kill $pid2 || st -t statusutil_cpu -g 82x25+$((mx - 328))+$((my + 20)) -c FGN -e btop 55 | } 56 | 57 | click() { 58 | case "$1" in 59 | L) notify ;; 60 | M) ;; 61 | R) call_btop ;; 62 | U) ;; 63 | D) ;; 64 | esac 65 | } 66 | 67 | case "$1" in 68 | click) click $2 ;; 69 | notify) notify ;; 70 | *) update ;; 71 | esac 72 | -------------------------------------------------------------------------------- /DEF/statusbar/statusbar.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | thisdir=$(cd $(dirname $0);pwd) 4 | tempfile=$thisdir/temp 5 | touch $tempfile 6 | 7 | # 设置某个模块的状态 update cpu mem ... 8 | update() { 9 | [ ! "$1" ] && refresh && return # 当指定模块为空时 结束 10 | bash $thisdir/packages/$1.sh # 执行指定模块脚本 11 | shift 1; update $* # 递归调用 12 | } 13 | 14 | # 处理状态栏点击 15 | click() { 16 | [ ! "$1" ] && return # 未传递参数时 结束 17 | bash $thisdir/packages/$1.sh click $2 # 执行指定模块脚本 18 | update $1 # 更新指定模块 19 | refresh # 刷新状态栏 20 | } 21 | 22 | # 更新状态栏 23 | refresh() { 24 | _icons='';_music='';_wifi='';_cpu='';_mem='';_date='';_vol='';_bat=''# 重置所有模块的状态为空 25 | source $tempfile # 从 temp 文件中读取模块的状态 26 | xsetroot -name "$_icons$_music$_wifi$_cpu$_mem$_date$_vol$_bat" # 更新状态栏 27 | } 28 | 29 | # 启动定时更新状态栏 不同的模块有不同的刷新周期 注意不要重复启动该func 30 | cron() { 31 | echo > $tempfile # 清空 temp 文件 32 | let i=0 33 | while true; do 34 | to=() # 存放本次需要更新的模块 35 | [ $((i % 10)) -eq 0 ] && to=(${to[@]} wifi) # 每 10秒 更新 wifi 36 | [ $((i % 20)) -eq 0 ] && to=(${to[@]} cpu mem vol icons) # 每 20秒 更新 cpu mem vol icons 37 | [ $((i % 300)) -eq 0 ] && to=(${to[@]} bat) # 每 300秒 更新 bat 38 | [ $((i % 5)) -eq 0 ] && to=(${to[@]} date music) # 每 5秒 更新 date 39 | [ $i -lt 30 ] && to=(wifi cpu mem date vol icons bat) # 前 30秒 更新所有模块 40 | update ${to[@]} # 将需要更新的模块传递给 update 41 | sleep 5; let i+=5 42 | done & 43 | } 44 | 45 | # cron 启动定时更新状态栏 46 | # update 更新指定模块 `update cpu` `update mem` `update date` `update vol` `update bat` 等 47 | # updateall 更新所有模块 | check 检查模块是否正常(行为等于updateall) 48 | # * 处理状态栏点击 `cpu 按键` `mem 按键` `date 按键` `vol 按键` `bat 按键` 等 49 | case $1 in 50 | cron) cron ;; 51 | update) shift 1; update $* ;; 52 | updateall|check) update icons music wifi cpu mem date vol bat ;; 53 | *) click $1 $2 ;; # 接收clickstatusbar传递过来的信号 $1: 模块名 $2: 按键(L|M|R|U|D) 54 | esac 55 | -------------------------------------------------------------------------------- /DEF/statusbar/packages/vol.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # VOL 音量脚本 3 | # 本脚本需要你自行修改音量获取命令 4 | # 例如我使用的是 pipewire 5 | # 6 | # $ pactl list sinks | grep RUNNING -A 8 7 | # State: RUNNING 8 | # Name: bluez_output.88_C9_E8_14_2A_72.1 9 | # Description: WH-1000XM4 10 | # Driver: PipeWire 11 | # Sample Specification: float32le 2ch 48000Hz 12 | # Channel Map: front-left,front-right 13 | # Owner Module: 4294967295 14 | # 静音 -> Mute: no 15 | # 音量 -> Volume: front-left: 13183 / 20% / -41.79 dB, front-right: 13183 / 20% / -41.79 dB 16 | 17 | tempfile=$(cd $(dirname $0);cd ..;pwd)/temp 18 | 19 | this=_vol 20 | icon_color="^c#442266^^b#7879560x88^" 21 | text_color="^c#442266^^b#7879560x99^" 22 | signal=$(echo "^s$this^" | sed 's/_//') 23 | 24 | # check 25 | [ ! "$(command -v pactl)" ] && echo command not found: pactl && exit 26 | 27 | update() { 28 | sink=$(pactl info | grep 'Default Sink' | awk '{print $3}') 29 | if [ "$sink" = "" ]; then sink=$(pactl info | grep '默认音频入口' | awk -F':' '{print $2}');fi 30 | volunmuted=$(pactl list sinks | grep $sink -A 6 | sed -n '7p' | grep '静音:否') 31 | vol_text=$(pactl list sinks | grep $sink -A 7 | sed -n '8p' | awk '{printf int($4)}') 32 | if [ "$LANG" != "zh_CN.UTF-8" ]; then 33 | volunmuted=$(pactl list sinks | grep $sink -A 6 | sed -n '7p' | grep 'Mute: no') 34 | vol_text=$(pactl list sinks | grep $sink -A 7 | sed -n '8p' | awk '{printf int($5)}') 35 | fi 36 | if [ ! "$volunmuted" ]; then vol_text="--"; vol_icon="ﱝ"; 37 | elif [ "$vol_text" -eq 0 ]; then vol_text="00"; vol_icon="婢"; 38 | elif [ "$vol_text" -lt 10 ]; then vol_icon="奔"; vol_text=0$vol_text; 39 | elif [ "$vol_text" -le 50 ]; then vol_icon="奔"; 40 | else vol_icon="墳"; fi 41 | 42 | icon=" $vol_icon " 43 | text=" $vol_text% " 44 | 45 | sed -i '/^export '$this'=.*$/d' $tempfile 46 | printf "export %s='%s%s%s%s%s'\n" $this "$signal" "$icon_color" "$icon" "$text_color" "$text" >> $tempfile 47 | } 48 | 49 | notify() { 50 | update 51 | notify-send -r 9527 -h int:value:$vol_text -h string:hlcolor:#dddddd "$vol_icon Volume" 52 | } 53 | 54 | click() { 55 | case "$1" in 56 | L) notify ;; # 仅通知 57 | M) pactl set-sink-mute @DEFAULT_SINK@ toggle ;; # 切换静音 58 | R) killall pavucontrol || pavucontrol --class=FGN & ;; # 打开pavucontrol 59 | U) pactl set-sink-volume @DEFAULT_SINK@ +5%; notify ;; # 音量加 60 | D) pactl set-sink-volume @DEFAULT_SINK@ -5%; notify ;; # 音量减 61 | esac 62 | } 63 | 64 | case "$1" in 65 | click) click $2 ;; 66 | notify) notify ;; 67 | *) update ;; 68 | esac 69 | -------------------------------------------------------------------------------- /DEF/statusbar/packages/date.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # DATE 获取日期和时间的脚本 3 | 4 | tempfile=$(cd $(dirname $0);cd ..;pwd)/temp 5 | 6 | this=_date 7 | icon_color="^c#4B005B^^b#7E51680x88^" 8 | text_color="^c#4B005B^^b#7E51680x99^" 9 | signal=$(echo "^s$this^" | sed 's/_//') 10 | 11 | update() { 12 | time_text="$(date '+%m/%d %H:%M')" 13 | case "$(date '+%I')" in 14 | "01") time_icon="" ;; 15 | "02") time_icon="" ;; 16 | "03") time_icon="" ;; 17 | "04") time_icon="" ;; 18 | "05") time_icon="" ;; 19 | "06") time_icon="" ;; 20 | "07") time_icon="" ;; 21 | "08") time_icon="" ;; 22 | "09") time_icon="" ;; 23 | "10") time_icon="" ;; 24 | "11") time_icon="" ;; 25 | "12") time_icon="" ;; 26 | esac 27 | 28 | icon=" $time_icon " 29 | text=" $time_text " 30 | 31 | sed -i '/^export '$this'=.*$/d' $tempfile 32 | printf "export %s='%s%s%s%s%s'\n" $this "$signal" "$icon_color" "$icon" "$text_color" "$text" >> $tempfile 33 | } 34 | 35 | notify() { 36 | d1="D:$(date '+%Y-%m-%d')"; d2="D:$(date -d '-1 day ago' '+%Y-%m-%d')"; d3="D:$(date -d '-2 day ago' '+%Y-%m-%d')" 37 | _cal=$(cal --color=always | sed 1,2d | sed 's/..7m//;s/..0m/<\/span><\/b>/' ) 38 | _all=$(cat ~/.todo.md | grep "\- \[.\]" | wc -l) 39 | _alltask=$(cat ~/.todo.md | grep "\- \[ \]" | sed 's/- \[ \] //' | sed 's/[SD]:.*//') 40 | _today=$(cat ~/.todo.md | grep "\- \[.\]" | grep "$d1" | wc -l) 41 | _near3day=$(cat ~/.todo.md | grep "\- \[.\]" | grep "$d1\|$d2\|$d3" | wc -l) 42 | _todaytask=$(cat ~/.todo.md | grep "\- \[ \]" | grep "$d1" | sed 's/- \[ \] /- /' | sed 's/[SD]:.*//') 43 | t1="任务:$_all" 44 | t2="近期:$_near3day" 45 | t3="今日:$_today" 46 | _todotext="$t1 $t2 $t3" 47 | [ "$_todaytask" ] && _todaytext="\n\n$_todaytask" 48 | [ ! "$_todaytask" ] && _todaytext="\n\n$_alltask" 49 | 50 | notify-send " Calendar" "\n$_cal\n\n$_todotext$_todaytext" -r 9527 51 | } 52 | 53 | call_todo() { 54 | pid1=`ps aux | grep 'st -t statusutil' | grep -v grep | awk '{print $2}'` 55 | pid2=`ps aux | grep 'st -t statusutil_todo' | grep -v grep | awk '{print $2}'` 56 | mx=`xdotool getmouselocation --shell | grep X= | sed 's/X=//'` 57 | my=`xdotool getmouselocation --shell | grep Y= | sed 's/Y=//'` 58 | kill $pid1 && kill $pid2 || st -t statusutil_todo -g 50x15+$((mx - 200))+$((my + 20)) -c FGN -e nvim ~/.todo.md 59 | } 60 | 61 | click() { 62 | case "$1" in 63 | L) notify ;; 64 | R) call_todo ;; 65 | esac 66 | } 67 | 68 | case "$1" in 69 | click) click $2 ;; 70 | notify) notify ;; 71 | *) update ;; 72 | esac 73 | -------------------------------------------------------------------------------- /DEF/statusbar/packages/bat.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 电池电量 3 | # 需要安装acpi或者upower 4 | 5 | tempfile=$(cd $(dirname $0);cd ..;pwd)/temp 6 | 7 | this=_bat 8 | icon_color="^c#3B001B^^b#4865660x88^" 9 | text_color="^c#3B001B^^b#4865660x99^" 10 | signal=$(echo "^s$this^" | sed 's/_//') 11 | 12 | get_by_acpi() { 13 | [ ! "$(command -v acpi)" ] && echo command not found: acpi && return 14 | bat_text=$(acpi -b | sed '2,$d' | awk '{print $4}' | grep -Eo "[0-9]+") 15 | [ ! "$bat_text" ] && bat_text=$(acpi -b | sed '2,$d' | awk -F'[ %]' '{print $5}' | grep -Eo "[0-9]+") 16 | [ ! "$(acpi -b | grep 'Battery 0' | grep Discharging)" ] && charging="true" 17 | _time="可用时间: $(acpi | sed 's/^Battery 0: //g' | awk -F ',' '{print $3}' | sed 's/^[ ]//g' | awk '{print $1}')" 18 | [ "$_time" = "可用时间: " ] && _time="" 19 | } 20 | 21 | get_by_upower() { 22 | [ ! "$(command -v upower)" ] && echo command not found: upower && return 23 | bat=$(upower -e | grep BAT) 24 | bat_text=$(upower -i $bat | awk '/percentage/ {print $2}' | grep -Eo '[0-9]+') 25 | [ "$(upower -i $bat | grep -w charging)" ] && charging="true" 26 | [ "$(upower -i $bat | grep 'state:.*fully-charged')" ] && charging="" 27 | } 28 | 29 | update() { 30 | get_by_upower 31 | get_by_acpi 32 | [ -z $bat_text ] && bat_text=0 33 | if [ "$charging" ]; then 34 | if [ "$bat_text" -ge 95 ]; then bat_icon=""; 35 | elif [ "$bat_text" -ge 90 ]; then bat_icon="󰂋"; 36 | elif [ "$bat_text" -ge 80 ]; then bat_icon="󰂊"; 37 | elif [ "$bat_text" -ge 70 ]; then bat_icon="󰢞"; 38 | elif [ "$bat_text" -ge 60 ]; then bat_icon="󰂉"; 39 | elif [ "$bat_text" -ge 50 ]; then bat_icon="󰢝"; 40 | elif [ "$bat_text" -ge 40 ]; then bat_icon="󰂈"; 41 | elif [ "$bat_text" -ge 30 ]; then bat_icon="󰂇"; 42 | elif [ "$bat_text" -ge 20 ]; then bat_icon="󰂆"; 43 | elif [ "$bat_text" -ge 10 ]; then bat_icon="󰢜"; 44 | else bat_icon="󰢟"; fi 45 | else 46 | if [ "$bat_text" -ge 95 ]; then bat_icon=""; 47 | elif [ "$bat_text" -ge 90 ]; then bat_icon=""; 48 | elif [ "$bat_text" -ge 80 ]; then bat_icon=""; 49 | elif [ "$bat_text" -ge 70 ]; then bat_icon=""; 50 | elif [ "$bat_text" -ge 60 ]; then bat_icon=""; 51 | elif [ "$bat_text" -ge 50 ]; then bat_icon=""; 52 | elif [ "$bat_text" -ge 40 ]; then bat_icon=""; 53 | elif [ "$bat_text" -ge 30 ]; then bat_icon=""; 54 | elif [ "$bat_text" -ge 20 ]; then bat_icon=""; 55 | elif [ "$bat_text" -ge 10 ]; then bat_icon=""; 56 | else bat_icon=""; fi 57 | fi 58 | 59 | icon=" $bat_icon " 60 | text=" $bat_text% " 61 | 62 | sed -i '/^export '$this'=.*$/d' $tempfile 63 | printf "export %s='%s%s%s%s%s'\n" $this "$signal" "$icon_color" "$icon" "$text_color" "$text" >> $tempfile 64 | } 65 | 66 | notify() { 67 | update 68 | if [ "$charging" ]; then _time=""; fi 69 | notify-send "$bat_icon Battery" "\n剩余: $bat_text%\n$_time" -r 9527 70 | } 71 | 72 | click() { 73 | case "$1" in 74 | L) notify ;; 75 | R) killall xfce4-power-manager-settings || setsid xfce4-power-manager-settings ;; 76 | esac 77 | } 78 | 79 | case "$1" in 80 | click) click $2 ;; 81 | notify) notify ;; 82 | *) update ;; 83 | esac 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DWM YES 2 | 3 | dwm 是一个非常快速, 小巧并使用动态管理窗口的窗口管理器 4 | 5 | [展示视频: BV1Ef4y1Z7kA](https://www.bilibili.com/video/BV1Ef4y1Z7kA/) 6 | 7 | ## 功能 8 | 9 | - 支持布局 tile(磁块)、magicgrid(进阶的网格布局) 10 | - 键盘移动/调整窗口大小 且移动/调整时有窗口间吸附效果 11 | - 窗口隐藏 12 | - 窗口可自定义是否全局(在所有tag内展示) 13 | - 更好的浮动窗口支持 14 | - 优化后的status2d 状态栏,可用鼠标点击操作 15 | - 系统托盘支持 16 | - overview 17 | - mod + tab, 在窗口间切换 有浮动窗口时仅在浮动窗口切换 18 | - mod + [tag], 切换tag到指定目录时 可指定一个cmd,若目标tag无窗口 则执行该tag 19 | 20 | ## 安装 21 | 22 | ```plaintext 23 | !!!首次使用 请 cp -r DEF/* . 24 | 25 | 每次修改源代码后都需要执行 26 | sudo make clean install 27 | ``` 28 | 29 | ## !!!运行 dwm!!! 30 | 31 | 请确保你已配置 ~/.xinitrc 文件, DWM指向你的dwm仓库所在路径 32 | 33 | ```plaintext 34 | export DWM=~/workspace/dwm 35 | exec dwm 36 | ``` 37 | 38 | tty中执行 `startx` 启动 39 | 40 | 如果想在tty1中自动执行startx可在你的bash或zsh配置中添加 41 | 42 | ```plaintext 43 | [ $(tty) = "/dev/tty1" ] && cd ~ && startx 44 | ``` 45 | 46 | ## !!!关于fork配置!!! 47 | 48 | ```plaintext 49 | 本仓库默认集成了 `DEF/` 目录,该目录为作者本人使用的配置 50 | DEF: 推荐配置 亦是 作者本人使用的配置 51 | 52 | 首次运行 可自行 `cp -r DEF/* .` 53 | 54 | 后续请用户自行维护 ./config.h ./statusbar ./autostart.sh 文件 55 | 且此部分文件已被 gitignore 56 | 57 | 即用户可始终保持yaocccc/dwm仓库代码最新版而不受影响 58 | 59 | 较推荐的fork方式 60 | 61 | 1. fork本代码仓库 62 | 2. 自行维护fork后的仓库相关的配置文件: ./config.h ./statusbar ./autostart.sh 63 | 3. 注释掉fork后仓库中 .gitignore 的前三行 64 | 4. 定期在github页面sync yaocccc/dwm 仓库保持最新 65 | ``` 66 | 67 | ### Nix Flake 68 | 69 | 下面是在 nixos configuration 中使用它的示例 70 | ```nix 71 | { 72 | description = "My configuration"; 73 | 74 | inputs = { 75 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 76 | dwm.url = "github:yaocccc/dwm"; 77 | }; 78 | 79 | outputs = { nixpkgs, dwm, ... }: 80 | { 81 | nixosConfigurations = { 82 | hostname = nixpkgs.lib.nixosSystem 83 | { 84 | system = "x86_64-linux"; 85 | modules = [ 86 | { 87 | nixpkgs.overlays = [ dwm.overlays.default ]; 88 | } 89 | ]; 90 | }; 91 | }; 92 | }; 93 | } 94 | ``` 95 | 96 | ## 状态栏 97 | 98 | 请将每一个块置为下列样式, 可直接使用本仓库statusbar相关脚本 或参考使用 99 | 100 | ```plaintext 101 | ^sdate^^c#2D1B46^^b#335566^  11/04 00:42 ^d^ 102 | 103 | ^s?????^ => 点击时的信号值 104 | ^c?????^ => 前景色 105 | ^b?????^ => 背景色 106 | ^d^ => 重置颜色 107 | 108 | 也可以直接从^c ^b 定义前景色 后景色透明度 109 | ^c#??????0xff^ => 0xff 前景色透明度 110 | ^b#??????0x11^ => 0x11 后景色透明度 111 | 112 | 本仓库维护了 statusbar脚本 入口为 ./statusbar/statusbar.sh 113 | 114 | 模块列表见 ./statusbar/packages 115 | 116 | 若需要使用 请逐个去查看 修改packages中的脚本文件 117 | 118 | 请在dwm启动时 调用 $DWM/statusbar/statusbar.sh cron 119 | 120 | 注意 ~/.profile中需要有 该环境变量为强依赖关系 121 | export DWM=~/workspace/dwm 122 | 123 | 点击事件发生时 会调用 $DWM/statusbar/statusbar.sh 并传入信号值 请自行处理 124 | 例如 $DWM/statusbar/statusbar.sh date L # 其中date为信号值 L为按键 (L左键 M中键 R右键) 125 | 126 | 可执行 $DWM/statusbar/statusbar.sh check 检查是否有模块存在问题 127 | ``` 128 | 129 | ## 随DWM启动的自启动命令 130 | 131 | dwm启动时会去调用 $DWM/autostart.sh 脚本 132 | 133 | 可参考 [autostart脚本](https://github.com/yaocccc/dwm/blob/master/DEF/autostart.sh) 134 | 135 | ## Q & A 136 | 137 | 1. 如何启动dwm? 138 | 139 | 确保 ~/.xinitrc 中有 exec dwm。在tty中使用 startx 命令启动 140 | 141 | 2. 进入后是黑屏啥都没 142 | 143 | 壁纸需要用类似feh的软件设置 `feh --randomize --bg-fill ~/pictures/*.png` 144 | 145 | 3. 打不开终端 146 | 147 | 务必先修改config.h中启动终端的快捷键,本项目的config.h是我自用的配置 你需要手动修改 148 | 149 | 例如 可以修改以下部分(如果你用的终端是st的话) 150 | 151 | ```plaintext 152 | /* spawn + SHCMD 执行对应命令 */ 153 | { MODKEY, XK_Return, spawn, SHCMD("st") }, 154 | ``` 155 | 156 | 4. 字体显示不全 157 | 158 | 请自行安装字体 仅以archlinux举例 159 | 160 | ```shell 161 | yay -S ttf-jetbrains-mono-nerd 162 | yay -S nerd-fonts-complete-mono-glyphs 163 | yay -S ttf-material-design-icons 164 | yay -S ttf-joypixels 165 | yay -S wqy-microhei 166 | ``` 167 | 168 | 5. 如果想使用tabbed管理st 169 | 170 | 推荐以下按键配置 171 | 172 | 关联链接 173 | [极简终端: https://github.com/yaocccc/st](https://github.com/yaocccc/st) 174 | [多tab管理: https://github.com/yaocccc/st](https://github.com/yaocccc/tabbed) 175 | 176 | ```c 177 | { MODKEY, XK_s, togglescratch, SHCMD("tabbed -n scratchpad -c -r 2 st -w ''") }, /* super s | 打开st scratchpad */ 178 | { MODKEY, XK_Return, spawn, SHCMD("tabbed -n st -C tabbed -c -r 2 st -w ''") }, /* super enter | 打开st */ 179 | { MODKEY, XK_minus, spawn, SHCMD("tabbed -n st -C FG -c -r 2 st -w ''") }, /* super + | 打开全局st终端 */ 180 | { MODKEY, XK_space, spawn, SHCMD("tabbed -n st -C float -c -r 2 st -w ''") }, /* super space | 打开浮动st终端 */ 181 | ``` 182 | 183 | 6. 为什么我的st各种奇怪问题 184 | 185 | 可以考虑先用我的 [yaocccc/st](https://github.com/yaocccc/st) 186 | 187 | 7. 自启动脚本没启动 188 | 189 | 请检查DWM变量是否正确设置,如果实在不行了,可以直接强制改config.h 里的 autostartscript 和 statusbarscript 变量 190 | 191 | ## 贡献者 THX 🌻 192 | 193 | - [yaocccc](https://github.com/yaocccc) 194 | - [MASTER](#TOP) 195 | - [p3psi-boo](https://github.com/p3psi-boo) 196 | - [PR#4 添加 Nix Flake 支持](https://github.com/yaocccc/dwm/pull/4) 197 | - [gxt-kt](https://github.com/gxt-kt) 198 | - [PR#7 修复hide/show窗口栈索引带来的无法恢复窗口的bug](https://github.com/yaocccc/dwm/pull/7) 199 | - [PR#19 二维选中、交换窗口](https://github.com/yaocccc/dwm/pull/19) 200 | - [Ruixi-rebirth](https://github.com/Ruixi-rebirth) 201 | - [PR#12 优化flake](https://github.com/yaocccc/dwm/pull/12) 202 | - [PR#16 优化flake](https://github.com/yaocccc/dwm/pull/16) 203 | - [Int-0X7FFFFFFF](https://github.com/Int-0X7FFFFFFF) 204 | - [PR#20 修复了音量在中文环境下一直显示静音的错误](https://github.com/yaocccc/dwm/pull/20) 205 | - [zainmiku](https://github.com/zainmiku) 206 | - [PR#25 音乐标题包含"'时的处理](https://github.com/yaocccc/dwm/pull/25) 207 | - [PR#29 音乐标题包含空格时的处理](https://github.com/yaocccc/dwm/pull/29) 208 | - [roukaixin](https://github.com/roukaixin) 209 | - [PR#42 修复仅有一个tile窗口时的边框切换逻辑](https://github.com/yaocccc/dwm/pull/42) 210 | - [luo216](https://github.com/luo216) 211 | - [diff:preview-all-win 预览窗口补丁](https://github.com/luo216/preview-all-win) 212 | 213 | ## ENJOY IT 😃 214 | -------------------------------------------------------------------------------- /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 | .SS Mouse commands 158 | .TP 159 | .B Mod1\-Button1 160 | Move focused window while dragging. Tiled windows will be toggled to the floating state. 161 | .TP 162 | .B Mod1\-Button2 163 | Toggles focused window between floating and tiled state. 164 | .TP 165 | .B Mod1\-Button3 166 | Resize focused window while dragging. Tiled windows will be toggled to the floating state. 167 | .SH CUSTOMIZATION 168 | dwm is customized by creating a custom config.h and (re)compiling the source 169 | code. This keeps it fast, secure and simple. 170 | .SH SEE ALSO 171 | .BR dmenu (1), 172 | .BR st (1) 173 | .SH ISSUES 174 | Java applications which use the XToolkit/XAWT backend may draw grey windows 175 | only. The XToolkit/XAWT backend breaks ICCCM-compliance in recent JDK 1.5 and early 176 | JDK 1.6 versions, because it assumes a reparenting window manager. Possible workarounds 177 | are using JDK 1.4 (which doesn't contain the XToolkit/XAWT backend) or setting the 178 | environment variable 179 | .BR AWT_TOOLKIT=MToolkit 180 | (to use the older Motif backend instead) or running 181 | .B xprop -root -f _NET_WM_NAME 32a -set _NET_WM_NAME LG3D 182 | or 183 | .B wmname LG3D 184 | (to pretend that a non-reparenting window manager is running that the 185 | XToolkit/XAWT backend can recognize) or when using OpenJDK setting the environment variable 186 | .BR _JAVA_AWT_WM_NONREPARENTING=1 . 187 | .SH BUGS 188 | Send all bug reports with a patch to hackers@suckless.org. 189 | -------------------------------------------------------------------------------- /drw.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "drw.h" 9 | #include "util.h" 10 | 11 | #define UTF_INVALID 0xFFFD 12 | #define UTF_SIZ 4 13 | 14 | static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; 15 | static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; 16 | static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; 17 | static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; 18 | 19 | static long 20 | utf8decodebyte(const char c, size_t *i) 21 | { 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 = 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 = 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 = 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 = 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 | -------------------------------------------------------------------------------- /DEF/config.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static int showsystray = 1; /* 是否显示托盘栏 */ 4 | static const int newclientathead = 0; /* 定义新窗口在栈顶还是栈底 */ 5 | static const int managetransientwin = 1; /* 是否管理临时窗口 */ 6 | static const unsigned int borderpx = 2; /* 窗口边框大小 */ 7 | static const unsigned int systraypinning = 1; /* 托盘跟随的显示器 0代表不指定显示器 */ 8 | static const unsigned int systrayspacing = 1; /* 托盘间距 */ 9 | static const unsigned int systrayspadding = 5; /* 托盘和状态栏的间隙 */ 10 | static int gappi = 12; /* 窗口与窗口 缝隙大小 */ 11 | static int gappo = 12; /* 窗口与边缘 缝隙大小 */ 12 | static const int _gappo = 12; /* 窗口与窗口 缝隙大小 不可变 用于恢复时的默认值 */ 13 | static const int _gappi = 12; /* 窗口与边缘 缝隙大小 不可变 用于恢复时的默认值 */ 14 | static const int vertpad = 5; /* vertical padding of bar */ 15 | static const int sidepad = 5; /* horizontal padding of bar */ 16 | static const int showbar = 1; /* 是否显示状态栏 */ 17 | static const int topbar = 1; /* 指定状态栏位置 0底部 1顶部 */ 18 | static const float mfact = 0.6; /* 主工作区 大小比例 */ 19 | static const int nmaster = 1; /* 主工作区 窗口数量 */ 20 | static const unsigned int snap = 10; /* 边缘依附宽度 */ 21 | static const unsigned int baralpha = 0xc0; /* 状态栏透明度 */ 22 | static const unsigned int borderalpha = 0xdd; /* 边框透明度 */ 23 | static const char *fonts[] = { "JetBrainsMono Nerd Font Mono:style=medium:size=13", "monospace:size=13" }; 24 | static const char *colors[][3] = { /* 颜色设置 ColFg, ColBg, ColBorder */ 25 | [SchemeNorm] = { "#bbbbbb", "#333333", "#444444" }, 26 | [SchemeSel] = { "#ffffff", "#37474F", "#42A5F5" }, 27 | [SchemeSelGlobal] = { "#ffffff", "#37474F", "#FFC0CB" }, 28 | [SchemeHid] = { "#dddddd", NULL, NULL }, 29 | [SchemeSystray] = { NULL, "#7799AA", NULL }, 30 | [SchemeUnderline] = { "#7799AA", NULL, NULL }, 31 | [SchemeNormTag] = { "#bbbbbb", "#333333", NULL }, 32 | [SchemeSelTag] = { "#eeeeee", "#333333", NULL }, 33 | [SchemeBarEmpty] = { NULL, "#111111", NULL }, 34 | }; 35 | static const unsigned int alphas[][3] = { /* 透明度设置 ColFg, ColBg, ColBorder */ 36 | [SchemeNorm] = { OPAQUE, baralpha, borderalpha }, 37 | [SchemeSel] = { OPAQUE, baralpha, borderalpha }, 38 | [SchemeSelGlobal] = { OPAQUE, baralpha, borderalpha }, 39 | [SchemeNormTag] = { OPAQUE, baralpha, borderalpha }, 40 | [SchemeSelTag] = { OPAQUE, baralpha, borderalpha }, 41 | [SchemeBarEmpty] = { 0, 0x11, 0 }, 42 | [SchemeStatusText] = { OPAQUE, 0x88, 0 }, 43 | }; 44 | 45 | /* 自定义脚本位置 */ 46 | static const char *autostartscript = "$DWM/autostart.sh"; 47 | static const char *statusbarscript = "$DWM/statusbar/statusbar.sh"; 48 | 49 | /* 自定义 scratchpad instance */ 50 | static const char scratchpadname[] = "scratchpad"; 51 | 52 | /* 自定义tag名称 */ 53 | /* 自定义特定实例的显示状态 */ 54 | //            ﮸  ﭮ 切 55 | static const char *tags[] = { 56 | "", // tag:0 key:1 desc:terminal1 57 | "", // tag:1 key:2 desc:terminal2 58 | "", // tag:2 key:3 desc:terminal3 59 | "󰕧", // tag:4 key:9 desc:obs 60 | "", // tag:5 key:c desc:chrome 61 | "", // tag:6 key:m desc:music 62 | "ffl", // tag:7 key:0 desc:qq 63 | "﬐", // tag:8 key:w desc:wechat 64 | "", // tag:9 key:l desc:wxwork 65 | }; 66 | 67 | /* 自定义窗口显示规则 */ 68 | /* class instance title 主要用于定位窗口适合哪个规则 */ 69 | /* tags mask 定义符合该规则的窗口的tag 0 表示当前tag */ 70 | /* isfloating 定义符合该规则的窗口是否浮动 */ 71 | /* isglobal 定义符合该规则的窗口是否全局浮动 */ 72 | /* isnoborder 定义符合该规则的窗口是否无边框 */ 73 | /* monitor 定义符合该规则的窗口显示在哪个显示器上 -1 为当前屏幕 */ 74 | /* floatposition 定义符合该规则的窗口显示的位置 0 中间,1到9分别为9宫格位置,例如1左上,9右下,3右上 */ 75 | static const Rule rules[] = { 76 | /* class instance title tags mask isfloating isglobal isnoborder monitor floatposition */ 77 | /** 优先级高 越在上面优先度越高 */ 78 | { NULL, NULL, "保存文件", 0, 1, 0, 0, -1, 0}, // 浏览器保存文件 浮动 79 | { NULL, NULL, "图片查看器", 0, 1, 0, 0, -1, 0}, // qq图片查看器 浮动 80 | { NULL, NULL, "图片查看", 0, 1, 0, 0, -1, 0}, // 微信图片查看器 浮动 81 | { NULL, NULL, "预览", 0, 1, 0, 0, -1, 0}, // 企业微信图片查看器 浮动 82 | { NULL, NULL, "Media viewer", 0, 1, 0, 0, -1, 0}, // tg图片查看器 浮动 83 | 84 | /** 普通优先度 */ 85 | {"obs", NULL, NULL, 1 << 3, 0, 0, 0, -1, 0}, // obs tag -> 󰕧 86 | {"chrome", NULL, NULL, 1 << 4, 0, 0, 0, -1, 0}, // chrome tag ->  87 | {"Chromium", NULL, NULL, 1 << 4, 0, 0, 0, -1, 0}, // Chromium tag ->  88 | {"music", NULL, NULL, 1 << 5, 1, 0, 1, -1, 0}, // music tag ->  浮动、无边框 89 | { NULL, "qq", NULL, 1 << 6, 0, 0, 1, -1, 0}, // qq tag -> ffl 无边框 90 | { NULL, "wechat.exe", NULL, 1 << 7, 0, 0, 1, -1, 0}, // wechat tag -> ﬐ 无边框 91 | { NULL, "wxwork.exe", NULL, 1 << 8, 0, 0, 1, -1, 0}, // workwechat tag ->  无边框 92 | {"Vncviewer", NULL, NULL, 0, 1, 0, 1, -1, 2}, // Vncviewer 浮动、无边框 屏幕顶部 93 | {"flameshot", NULL, NULL, 0, 1, 0, 0, -1, 0}, // 火焰截图 浮动 94 | {"scratchpad", "scratchpad", "scratchpad", TAGMASK, 1, 1, 1, -1, 2}, // scratchpad 浮动、全局、无边框 屏幕顶部 95 | {"Pcmanfm", NULL, NULL, 0, 1, 0, 1, -1, 3}, // pcmanfm 浮动、无边框 右上角 96 | {"wemeetapp", NULL, NULL, TAGMASK, 1, 1, 0, -1, 0}, // !!!腾讯会议在切换tag时有诡异bug导致退出 变成global来规避该问题 97 | { NULL, NULL, "wechat", TAGMASK, 0, 0, 1, -1, 0}, // wechat相关的子窗口,去掉边框 98 | 99 | /** 部分特殊class的规则 */ 100 | {"float", NULL, NULL, 0, 1, 0, 0, -1, 0}, // class = float 浮动 101 | {"global", NULL, NULL, TAGMASK, 0, 1, 0, -1, 0}, // class = gloabl 全局 102 | {"noborder", NULL, NULL, 0, 0, 0, 1, -1, 0}, // class = noborder 无边框 103 | {"FGN", NULL, NULL, TAGMASK, 1, 1, 1, -1, 0}, // class = FGN 浮动、全局、无边框 104 | {"FG", NULL, NULL, TAGMASK, 1, 1, 0, -1, 0}, // class = FG 浮动、全局 105 | {"FN", NULL, NULL, 0, 1, 0, 1, -1, 0}, // class = FN 浮动、无边框 106 | {"GN", NULL, NULL, TAGMASK, 0, 1, 1, -1, 0}, // CLASS = GN 全局、无边框 107 | 108 | /** 优先度低 越在上面优先度越低 */ 109 | { NULL, NULL, "crx_", 0, 1, 0, 0, -1, 0}, // 错误载入时 会有crx_ 浮动 110 | { NULL, NULL, "broken", 0, 1, 0, 0, -1, 0}, // 错误载入时 会有broken 浮动 111 | }; 112 | 113 | /* 自定义布局 */ 114 | static const Layout layouts[] = { 115 | { "﬿", tile }, /* 主次栈 */ 116 | { "﩯", magicgrid }, /* 网格 */ 117 | }; 118 | 119 | #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } 120 | #define MODKEY Mod4Mask 121 | #define TAGKEYS(KEY, TAG, cmd) \ 122 | { MODKEY, KEY, view, {.ui = 1 << TAG, .v = cmd} }, \ 123 | { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ 124 | { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ 125 | 126 | static Key keys[] = { 127 | /* modifier key function argument */ 128 | { MODKEY, XK_equal, togglesystray, {0} }, /* super + | 切换 托盘栏显示状态 */ 129 | 130 | { MODKEY, XK_Tab, focusstack, {.i = +1} }, /* super tab | 本tag内切换聚焦窗口 */ 131 | { MODKEY|ShiftMask, XK_Tab, focusstack, {.i = -1} }, /* super shift tab | 本tag内切换聚焦窗口 */ 132 | { MODKEY, XK_Up, focusstack, {.i = -1} }, /* super up | 本tag内切换聚焦窗口 */ 133 | { MODKEY, XK_Down, focusstack, {.i = +1} }, /* super down | 本tag内切换聚焦窗口 */ 134 | 135 | { MODKEY, XK_Left, viewtoleft, {0} }, /* super left | 聚焦到左边的tag */ 136 | { MODKEY, XK_Right, viewtoright, {0} }, /* super right | 聚焦到右边的tag */ 137 | { MODKEY|ShiftMask, XK_Left, tagtoleft, {0} }, /* super shift left | 将本窗口移动到左边tag */ 138 | { MODKEY|ShiftMask, XK_Right, tagtoright, {0} }, /* super shift right | 将本窗口移动到右边tag */ 139 | 140 | { MODKEY, XK_a, previewallwin, {0} }, /* super a | overview */ 141 | 142 | { MODKEY, XK_comma, setmfact, {.f = -0.05} }, /* super , | 缩小主工作区 */ 143 | { MODKEY, XK_period, setmfact, {.f = +0.05} }, /* super . | 放大主工作区 */ 144 | 145 | { MODKEY, XK_i, hidewin, {0} }, /* super i | 隐藏 窗口 */ 146 | { MODKEY|ShiftMask, XK_i, restorewin, {0} }, /* super shift i | 取消隐藏 窗口 */ 147 | 148 | { MODKEY|ShiftMask, XK_Return, zoom, {0} }, /* super shift enter | 将当前聚焦窗口置为主窗口 */ 149 | 150 | { MODKEY, XK_t, togglefloating, {0} }, /* super t | 开启/关闭 聚焦目标的float模式 */ 151 | { MODKEY|ShiftMask, XK_t, toggleallfloating,{0} }, /* super shift t | 开启/关闭 全部目标的float模式 */ 152 | { MODKEY, XK_f, fullscreen, {0} }, /* super f | 开启/关闭 全屏 */ 153 | { MODKEY|ShiftMask, XK_f, togglebar, {0} }, /* super shift f | 开启/关闭 状态栏 */ 154 | { MODKEY, XK_g, toggleglobal, {0} }, /* super g | 开启/关闭 全局 */ 155 | { MODKEY, XK_u, toggleborder, {0} }, /* super u | 开启/关闭 边框 */ 156 | { MODKEY, XK_e, incnmaster, {.i = +1} }, /* super e | 改变主工作区窗口数量 (1 2中切换) */ 157 | 158 | { MODKEY, XK_b, focusmon, {.i = +1} }, /* super b | 光标移动到另一个显示器 */ 159 | { MODKEY|ShiftMask, XK_b, tagmon, {.i = +1} }, /* super shift b | 将聚焦窗口移动到另一个显示器 */ 160 | 161 | { MODKEY, XK_q, killclient, {0} }, /* super q | 关闭窗口 */ 162 | { MODKEY|ControlMask, XK_q, forcekillclient, {0} }, /* super ctrl q | 强制关闭窗口(处理某些情况下无法销毁的窗口) */ 163 | { MODKEY|ControlMask, XK_F12, quit, {0} }, /* super ctrl f12 | 退出dwm */ 164 | 165 | { MODKEY|ShiftMask, XK_space, selectlayout, {.v = &layouts[1]} }, /* super shift space | 切换到网格布局 */ 166 | { MODKEY, XK_o, showonlyorall, {0} }, /* super o | 切换 只显示一个窗口 / 全部显示 */ 167 | 168 | { MODKEY|ControlMask, XK_equal, setgap, {.i = -6} }, /* super ctrl + | 窗口增大 */ 169 | { MODKEY|ControlMask, XK_minus, setgap, {.i = +6} }, /* super ctrl - | 窗口减小 */ 170 | { MODKEY|ControlMask, XK_space, setgap, {.i = 0} }, /* super ctrl space | 窗口重置 */ 171 | 172 | { MODKEY|ControlMask, XK_Up, movewin, {.ui = UP} }, /* super ctrl up | 移动窗口 */ 173 | { MODKEY|ControlMask, XK_Down, movewin, {.ui = DOWN} }, /* super ctrl down | 移动窗口 */ 174 | { MODKEY|ControlMask, XK_Left, movewin, {.ui = LEFT} }, /* super ctrl left | 移动窗口 */ 175 | { MODKEY|ControlMask, XK_Right, movewin, {.ui = RIGHT} }, /* super ctrl right | 移动窗口 */ 176 | 177 | { MODKEY|Mod1Mask, XK_Up, resizewin, {.ui = V_REDUCE} }, /* super alt up | 调整窗口 */ 178 | { MODKEY|Mod1Mask, XK_Down, resizewin, {.ui = V_EXPAND} }, /* super alt down | 调整窗口 */ 179 | { MODKEY|Mod1Mask, XK_Left, resizewin, {.ui = H_REDUCE} }, /* super alt left | 调整窗口 */ 180 | { MODKEY|Mod1Mask, XK_Right, resizewin, {.ui = H_EXPAND} }, /* super alt right | 调整窗口 */ 181 | 182 | { MODKEY, XK_k, focusdir, {.i = UP } }, /* super k | 二维聚焦窗口 */ 183 | { MODKEY, XK_j, focusdir, {.i = DOWN } }, /* super j | 二维聚焦窗口 */ 184 | { MODKEY, XK_h, focusdir, {.i = LEFT } }, /* super h | 二维聚焦窗口 */ 185 | { MODKEY, XK_l, focusdir, {.i = RIGHT } }, /* super l | 二维聚焦窗口 */ 186 | { MODKEY|ShiftMask, XK_k, exchange_client, {.i = UP } }, /* super shift k | 二维交换窗口 (仅平铺) */ 187 | { MODKEY|ShiftMask, XK_j, exchange_client, {.i = DOWN } }, /* super shift j | 二维交换窗口 (仅平铺) */ 188 | { MODKEY|ShiftMask, XK_h, exchange_client, {.i = LEFT} }, /* super shift h | 二维交换窗口 (仅平铺) */ 189 | { MODKEY|ShiftMask, XK_l, exchange_client, {.i = RIGHT } }, /* super shift l | 二维交换窗口 (仅平铺) */ 190 | 191 | /* spawn + SHCMD 执行对应命令(已下部分建议完全自己重新定义) */ 192 | { MODKEY, XK_s, togglescratch, SHCMD("st -t scratchpad -c float") }, /* super s | 打开scratch终端 */ 193 | { MODKEY, XK_Return, spawn, SHCMD("st") }, /* super enter | 打开st终端 */ 194 | { MODKEY, XK_minus, spawn, SHCMD("st -c FG") }, /* super + | 打开全局st终端 */ 195 | { MODKEY, XK_space, spawn, SHCMD("st -c float") }, /* super space | 打开浮动st终端 */ 196 | { MODKEY, XK_F1, spawn, SHCMD("killall pcmanfm || pcmanfm") }, /* super F1 | 打开/关闭pcmanfm */ 197 | { MODKEY, XK_d, spawn, SHCMD("rofi -show run") }, /* super d | rofi: 执行run */ 198 | { MODKEY, XK_p, spawn, SHCMD("$DWM/DEF/rofi.sh") }, /* super p | rofi: 执行自定义脚本 */ 199 | { MODKEY, XK_n, spawn, SHCMD("$DWM/DEF/blurlock.sh") }, /* super n | 锁定屏幕 */ 200 | { MODKEY|ShiftMask, XK_Up, spawn, SHCMD("$DWM/DEF/set_vol.sh up") }, /* super shift up | 音量加 */ 201 | { MODKEY|ShiftMask, XK_Down, spawn, SHCMD("$DWM/DEF/set_vol.sh down") }, /* super shift down | 音量减 */ 202 | { MODKEY|ShiftMask, XK_a, spawn, SHCMD("flameshot gui -c -p ~/Pictures/screenshots") }, /* super shift a | 截图 */ 203 | { MODKEY|ShiftMask, XK_q, spawn, SHCMD("kill -9 $(xprop | grep _NET_WM_PID | awk '{print $3}')") }, /* super shift q | 选中某个窗口并强制kill */ 204 | 205 | /* super key : 跳转到对应tag (可附加一条命令 若目标目录无窗口,则执行该命令) */ 206 | /* super shift key : 将聚焦窗口移动到对应tag */ 207 | /* key tag cmd */ 208 | TAGKEYS(XK_1, 0, 0) 209 | TAGKEYS(XK_2, 1, 0) 210 | TAGKEYS(XK_3, 2, 0) 211 | TAGKEYS(XK_9, 3, "obs") 212 | TAGKEYS(XK_c, 4, "google-chrome-stable") 213 | TAGKEYS(XK_m, 5, "~/scripts/music_player.sh") 214 | TAGKEYS(XK_0, 6, "linuxqq") 215 | }; 216 | 217 | static Button buttons[] = { 218 | /* click event mask button function argument */ 219 | /* 点击窗口标题栏操作 */ 220 | { ClkWinTitle, 0, Button1, hideotherwins, {0} }, // 左键 | 点击标题 | 隐藏其他窗口仅保留该窗口 221 | { ClkWinTitle, 0, Button3, togglewin, {0} }, // 右键 | 点击标题 | 切换窗口显示状态 222 | /* 点击窗口操作 */ 223 | { ClkClientWin, MODKEY, Button1, movemouse, {0} }, // super+左键 | 拖拽窗口 | 拖拽窗口 224 | { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, // super+右键 | 拖拽窗口 | 改变窗口大小 225 | /* 点击tag操作 */ 226 | { ClkTagBar, 0, Button1, view, {0} }, // 左键 | 点击tag | 切换tag 227 | { ClkTagBar, 0, Button3, toggleview, {0} }, // 右键 | 点击tag | 切换是否显示tag 228 | { ClkTagBar, MODKEY, Button1, tag, {0} }, // super+左键 | 点击tag | 将窗口移动到对应tag 229 | { ClkTagBar, 0, Button4, viewtoleft, {0} }, // 鼠标滚轮上 | tag | 向前切换tag 230 | { ClkTagBar, 0, Button5, viewtoright, {0} }, // 鼠标滚轮下 | tag | 向后切换tag 231 | /* 点击状态栏操作 */ 232 | { ClkStatusText, 0, Button1, clickstatusbar,{0} }, // 左键 | 点击状态栏 | 根据状态栏的信号执行 ~/scripts/dwmstatusbar.sh $signal L 233 | { ClkStatusText, 0, Button2, clickstatusbar,{0} }, // 中键 | 点击状态栏 | 根据状态栏的信号执行 ~/scripts/dwmstatusbar.sh $signal M 234 | { ClkStatusText, 0, Button3, clickstatusbar,{0} }, // 右键 | 点击状态栏 | 根据状态栏的信号执行 ~/scripts/dwmstatusbar.sh $signal R 235 | { ClkStatusText, 0, Button4, clickstatusbar,{0} }, // 鼠标滚轮上 | 状态栏 | 根据状态栏的信号执行 ~/scripts/dwmstatusbar.sh $signal U 236 | { ClkStatusText, 0, Button5, clickstatusbar,{0} }, // 鼠标滚轮下 | 状态栏 | 根据状态栏的信号执行 ~/scripts/dwmstatusbar.sh $signal D 237 | // 238 | /* 点击bar空白处 */ 239 | { ClkBarEmpty, 0, Button1, spawn, SHCMD("~/scripts/call_rofi.sh window") }, // 左键 | bar空白处 | rofi 执行 window 240 | { ClkBarEmpty, 0, Button3, spawn, SHCMD("~/scripts/call_rofi.sh drun") }, // 右键 | bar空白处 | rofi 执行 drun 241 | // 242 | /* 鼠标在空白处或任意窗口上 上下滚动 切换tag */ 243 | { ClkRootWin, MODKEY, Button4, viewtoleft, {0} }, // super+滚轮上 | Any | 向前切换tag 244 | { ClkRootWin, MODKEY, Button5, viewtoright, {0} }, // super+滚轮下 | Any | 向后切换tag 245 | { ClkWinTitle, MODKEY, Button4, viewtoleft, {0} }, // super+滚轮上 | Any | 向前切换tag 246 | { ClkWinTitle, MODKEY, Button5, viewtoright, {0} }, // super+滚轮下 | Any | 向后切换tag 247 | { ClkClientWin, MODKEY, Button4, viewtoleft, {0} }, // super+滚轮上 | Any | 向前切换tag 248 | { ClkClientWin, MODKEY, Button5, viewtoright, {0} }, // super+滚轮下 | Any | 向后切换tag 249 | { ClkTagBar, MODKEY, Button4, viewtoleft, {0} }, // super+滚轮上 | Any | 向前切换tag 250 | { ClkTagBar, MODKEY, Button5, viewtoright, {0} }, // super+滚轮下 | Any | 向后切换tag 251 | { ClkStatusText, MODKEY, Button4, viewtoleft, {0} }, // super+滚轮上 | Any | 向前切换tag 252 | { ClkStatusText, MODKEY, Button5, viewtoright, {0} }, // super+滚轮下 | Any | 向后切换tag 253 | }; 254 | -------------------------------------------------------------------------------- /dwm.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. 2 | * 3 | * dynamic window manager is designed like any other X client as well. It is 4 | * driven through handling X events. In contrast to other X clients, a window 5 | * manager selects for SubstructureRedirectMask on the root window, to receive 6 | * events about window (dis-)appearance. Only one X connection at a time is 7 | * allowed to select for this event mask. 8 | * 9 | * The event handlers of dwm are organized in an array which is accessed 10 | * whenever a new event has been fetched. This allows event dispatching 11 | * in O(1) time. 12 | * 13 | * Each child of the root window is called a client, except windows which have 14 | * set the override_redirect flag. Clients are organized in a linked client 15 | * list on each monitor, the focus history is remembered through a stack list 16 | * on each monitor. Each client contains a bit array to indicate the tags of a 17 | * client. 18 | * 19 | * Keys and tagging rules are organized as arrays and defined in config.h. 20 | * 21 | * To understand everything else, start reading main(). 22 | */ 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #ifdef XINERAMA 42 | #include 43 | #endif /* XINERAMA */ 44 | #include 45 | #include 46 | 47 | #include "drw.h" 48 | #include "util.h" 49 | 50 | /* macros */ 51 | #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) 52 | #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) 53 | #define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ 54 | * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) 55 | #define ISVISIBLE(C) ((C->isglobal || C->tags & C->mon->tagset[C->mon->seltags])) 56 | #define HIDDEN(C) ((getstate(C->win) == IconicState)) 57 | #define LENGTH(X) (sizeof X / sizeof X[0]) 58 | #define MOUSEMASK (BUTTONMASK|PointerMotionMask) 59 | #define WIDTH(X) ((X)->w + 2 * (X)->bw) 60 | #define HEIGHT(X) ((X)->h + 2 * (X)->bw) 61 | #define TAGMASK ((1 << LENGTH(tags)) - 1) 62 | #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) 63 | #define SYSTEM_TRAY_REQUEST_DOCK 0 64 | 65 | /* XEMBED messages */ 66 | #define XEMBED_EMBEDDED_NOTIFY 0 67 | #define XEMBED_WINDOW_ACTIVATE 1 68 | #define XEMBED_FOCUS_IN 4 69 | #define XEMBED_MODALITY_ON 10 70 | 71 | #define XEMBED_MAPPED (1 << 0) 72 | #define XEMBED_WINDOW_ACTIVATE 1 73 | #define XEMBED_WINDOW_DEACTIVATE 2 74 | 75 | #define VERSION_MAJOR 0 76 | #define VERSION_MINOR 0 77 | #define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR 78 | 79 | #define OPAQUE 0xffU 80 | 81 | /* enums */ 82 | enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ 83 | enum { 84 | SchemeNorm, // 普通 85 | SchemeSel, // 选中的 86 | SchemeSelGlobal, // 全局并选中的 87 | SchemeHid, // 隐藏的 88 | SchemeSystray, // 托盘 89 | SchemeNormTag, // 普通标签 90 | SchemeSelTag, // 选中的标签 91 | SchemeUnderline, // 下划线 92 | SchemeBarEmpty, // 状态栏空白部分 93 | SchemeStatusText // 状态栏文本 94 | }; /* color schemes */ 95 | enum { NetSupported, NetWMName, NetWMState, NetWMCheck, 96 | NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz, 97 | NetWMFullscreen, NetActiveWindow, NetWMWindowType, 98 | NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ 99 | enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ 100 | enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ 101 | enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkBarEmpty, 102 | ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ 103 | enum { UP, DOWN, LEFT, RIGHT }; /* movewin */ 104 | enum { V_EXPAND, V_REDUCE, H_EXPAND, H_REDUCE }; /* resizewins */ 105 | 106 | typedef struct { 107 | int i; 108 | unsigned int ui; 109 | float f; 110 | const void *v; 111 | } Arg; 112 | 113 | typedef struct { 114 | unsigned int click; 115 | unsigned int mask; 116 | unsigned int button; 117 | void (*func)(const Arg *arg); 118 | const Arg arg; 119 | } Button; 120 | 121 | typedef struct Monitor Monitor; 122 | typedef struct Client Client; 123 | typedef struct Preview Preview; 124 | 125 | struct Preview { 126 | XImage *scaled_image; 127 | Window win; 128 | unsigned int x, y; 129 | }; 130 | 131 | struct Client { 132 | char name[256]; 133 | float mina, maxa; 134 | int x, y, w, h; 135 | int oldx, oldy, oldw, oldh; 136 | int basew, baseh, incw, inch, maxw, maxh, minw, minh; 137 | int bw, oldbw; 138 | int taskw; 139 | unsigned int tags; 140 | int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, isglobal, isnoborder, isscratchpad; 141 | Client *next; 142 | Client *snext; 143 | Monitor *mon; 144 | Window win; 145 | Preview preview; 146 | }; 147 | 148 | typedef struct { 149 | unsigned int mod; 150 | KeySym keysym; 151 | void (*func)(const Arg *); 152 | const Arg arg; 153 | } Key; 154 | 155 | typedef struct { 156 | const char *symbol; 157 | void (*arrange)(Monitor *); 158 | } Layout; 159 | 160 | typedef struct Pertag Pertag; 161 | struct Monitor { 162 | char ltsymbol[16]; 163 | float mfact; 164 | int nmaster; 165 | int num; 166 | int by; /* bar geometry */ 167 | int bt; /* number of tasks */ 168 | int mx, my, mw, mh; /* screen size */ 169 | int wx, wy, ww, wh; /* window area */ 170 | unsigned int seltags; 171 | unsigned int sellt; 172 | unsigned int tagset[2]; 173 | int showbar; 174 | int topbar; 175 | Client *clients; 176 | Client *sel; 177 | Client *stack; 178 | Monitor *next; 179 | Window barwin; 180 | const Layout *lt[2]; 181 | Pertag *pertag; 182 | }; 183 | 184 | typedef struct { 185 | const char *class; 186 | const char *instance; 187 | const char *title; 188 | unsigned int tags; 189 | int isfloating; 190 | int isglobal; 191 | int isnoborder; 192 | int monitor; 193 | uint floatposition; 194 | } Rule; 195 | 196 | typedef struct Systray Systray; 197 | struct Systray { 198 | Window win; 199 | Client *icons; 200 | }; 201 | 202 | /* function declarations */ 203 | static void logtofile(const char *fmt, ...); 204 | 205 | static void tile(Monitor *m); 206 | static void magicgrid(Monitor *m); 207 | static void grid(Monitor *m, uint gappo, uint uappi); 208 | 209 | static void applyrules(Client *c); 210 | static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); 211 | static void arrange(Monitor *m); 212 | static void arrangemon(Monitor *m); 213 | static void attach(Client *c); 214 | static void attachstack(Client *c); 215 | static void buttonpress(XEvent *e); 216 | static void checkotherwm(void); 217 | static void cleanup(void); 218 | static void cleanupmon(Monitor *mon); 219 | static void clientmessage(XEvent *e); 220 | static void configure(Client *c); 221 | static void configurenotify(XEvent *e); 222 | static void configurerequest(XEvent *e); 223 | static void clickstatusbar(const Arg *arg); 224 | static Monitor *createmon(void); 225 | static void destroynotify(XEvent *e); 226 | static void detach(Client *c); 227 | static void detachstack(Client *c); 228 | static Monitor *dirtomon(int dir); 229 | 230 | static void drawbar(Monitor *m); 231 | static void drawbars(void); 232 | static int drawstatusbar(Monitor *m, int bh, char* text); 233 | 234 | static void enternotify(XEvent *e); 235 | static void expose(XEvent *e); 236 | 237 | static void focusin(XEvent *e); 238 | static void focus(Client *c); 239 | static void focusmon(const Arg *arg); 240 | static void focusstack(const Arg *arg); 241 | 242 | static void pointerclient(Client *c); 243 | 244 | static Atom getatomprop(Client *c, Atom prop); 245 | static int getrootptr(int *x, int *y); 246 | static long getstate(Window w); 247 | static unsigned int getsystraywidth(); 248 | static int gettextprop(Window w, Atom atom, char *text, unsigned int size); 249 | 250 | static void grabbuttons(Client *c, int focused); 251 | static void grabkeys(void); 252 | 253 | static void hide(Client *c); 254 | static void show(Client *c); 255 | static void showtag(Client *c); 256 | static void hidewin(const Arg *arg); 257 | static void hideotherwins(const Arg *arg); 258 | static void showonlyorall(const Arg *arg); 259 | static int issinglewin(const Arg *arg); 260 | static void restorewin(const Arg *arg); 261 | 262 | static void incnmaster(const Arg *arg); 263 | static void keypress(XEvent *e); 264 | static void killclient(const Arg *arg); 265 | static void forcekillclient(const Arg *arg); 266 | 267 | static void manage(Window w, XWindowAttributes *wa); 268 | static void mappingnotify(XEvent *e); 269 | static void maprequest(XEvent *e); 270 | static void motionnotify(XEvent *e); 271 | static void movemouse(const Arg *arg); 272 | static void movewin(const Arg *arg); 273 | static void resizewin(const Arg *arg); 274 | static Client *nexttiled(Client *c); 275 | static void pop(Client *); 276 | static void propertynotify(XEvent *e); 277 | static void quit(const Arg *arg); 278 | static void setup(void); 279 | static void seturgent(Client *c, int urg); 280 | static void sigchld(int unused); 281 | static void spawn(const Arg *arg); 282 | static Monitor *systraytomon(Monitor *m); 283 | 284 | static Monitor *recttomon(int x, int y, int w, int h); 285 | static void removesystrayicon(Client *i); 286 | static void resize(Client *c, int x, int y, int w, int h, int interact); 287 | static void resizebarwin(Monitor *m); 288 | static void resizeclient(Client *c, int x, int y, int w, int h); 289 | static void resizemouse(const Arg *arg); 290 | static void resizerequest(XEvent *e); 291 | static void restack(Monitor *m); 292 | 293 | static void run(void); 294 | static void runAutostart(void); 295 | static void scan(void); 296 | static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4); 297 | static void sendmon(Client *c, Monitor *m); 298 | static void setclientstate(Client *c, long state); 299 | static void setfocus(Client *c); 300 | 301 | static void selectlayout(const Arg *arg); 302 | static void setlayout(const Arg *arg); 303 | 304 | static void fullscreen(const Arg *arg); 305 | static void setfullscreen(Client *c, int fullscreen); 306 | static void setmfact(const Arg *arg); 307 | 308 | static void tag(const Arg *arg); 309 | static void tagmon(const Arg *arg); 310 | static void tagtoleft(const Arg *arg); 311 | static void tagtoright(const Arg *arg); 312 | 313 | static void togglebar(const Arg *arg); 314 | static void togglesystray(); 315 | static void togglefloating(const Arg *arg); 316 | static void toggleallfloating(const Arg *arg); 317 | static void togglescratch(const Arg *arg); 318 | static void toggleview(const Arg *arg); 319 | static void togglewin(const Arg *arg); 320 | static void toggleglobal(const Arg *arg); 321 | static void toggleborder(const Arg *arg); 322 | 323 | static void unfocus(Client *c, int setfocus); 324 | static void unmanage(Client *c, int destroyed); 325 | static void unmapnotify(XEvent *e); 326 | 327 | static void updatebarpos(Monitor *m); 328 | static void updatebars(void); 329 | static void updateclientlist(void); 330 | static int updategeom(void); 331 | static void updatenumlockmask(void); 332 | static void updatesizehints(Client *c); 333 | static void updatestatus(void); 334 | static void updatesystray(void); 335 | static void updatesystrayicongeom(Client *i, int w, int h); 336 | static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); 337 | static void updatetitle(Client *c); 338 | static void updatewindowtype(Client *c); 339 | static void updatewmhints(Client *c); 340 | 341 | static void setgap(const Arg *arg); 342 | 343 | static void view(const Arg *arg); 344 | static void viewtoleft(const Arg *arg); 345 | static void viewtoright(const Arg *arg); 346 | 347 | static void exchange_client(const Arg *arg); 348 | static void focusdir(const Arg *arg); 349 | 350 | static Client *wintoclient(Window w); 351 | static Monitor *wintomon(Window w); 352 | static Client *wintosystrayicon(Window w); 353 | static int xerror(Display *dpy, XErrorEvent *ee); 354 | static int xerrordummy(Display *dpy, XErrorEvent *ee); 355 | static int xerrorstart(Display *dpy, XErrorEvent *ee); 356 | static void xinitvisual(); 357 | static void zoom(const Arg *arg); 358 | static void previewallwin(); 359 | static void setpreviewwins(unsigned int n, Monitor *m, unsigned int gappo, unsigned int gappi); 360 | static void focuspreviewwin(Client *focus_c, Monitor *m); 361 | static XImage *getwindowximage(Client *c); 362 | static XImage *scaledownimage(Client *c, unsigned int cw, unsigned int ch); 363 | 364 | /* variables */ 365 | static Systray *systray = NULL; 366 | static const char broken[] = "broken"; 367 | static char stext[1024]; 368 | static int screen; 369 | static int sw, sh; /* X display screen geometry width, height */ 370 | static int bh, blw = 0; /* bar geometry */ 371 | static int lrpad; /* sum of left and right padding for text */ 372 | static int vp; /* vertical padding for bar */ 373 | static int sp; /* side padding for bar */ 374 | static int (*xerrorxlib)(Display *, XErrorEvent *); 375 | static unsigned int numlockmask = 0; 376 | static void (*handler[LASTEvent]) (XEvent *) = { 377 | [ButtonPress] = buttonpress, 378 | [ClientMessage] = clientmessage, 379 | [ConfigureRequest] = configurerequest, 380 | [ConfigureNotify] = configurenotify, 381 | [DestroyNotify] = destroynotify, 382 | [EnterNotify] = enternotify, 383 | [Expose] = expose, 384 | [FocusIn] = focusin, 385 | [KeyPress] = keypress, 386 | [MappingNotify] = mappingnotify, 387 | [MapRequest] = maprequest, 388 | [MotionNotify] = motionnotify, 389 | [PropertyNotify] = propertynotify, 390 | [ResizeRequest] = resizerequest, 391 | [UnmapNotify] = unmapnotify 392 | }; 393 | static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; 394 | static int running = 1; 395 | static Cur *cursor[CurLast]; 396 | static Clr **scheme; 397 | static Display *dpy; 398 | static Drw *drw; 399 | static int useargb = 0; 400 | static Visual *visual; 401 | static int depth; 402 | static Colormap cmap; 403 | static Monitor *mons, *selmon; 404 | static Window root, wmcheckwin; 405 | 406 | static int hiddenWinStackTop = -1; 407 | static Client *hiddenWinStack[100]; 408 | 409 | /* configuration, allows nested code to access above variables */ 410 | #include "config.h" 411 | 412 | struct Pertag { 413 | unsigned int curtag, prevtag; /* current and previous tag */ 414 | int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ 415 | float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */ 416 | unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */ 417 | const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */ 418 | int showbars[LENGTH(tags) + 1]; /* display bar for the current tag */ 419 | }; 420 | 421 | /* function implementations */ 422 | void 423 | logtofile(const char *fmt, ...) 424 | { 425 | char buf[256]; 426 | char cmd[256]; 427 | va_list ap; 428 | va_start(ap, fmt); 429 | vsprintf((char *)buf, fmt, ap); 430 | va_end(ap); 431 | uint i = strlen((const char *)buf); 432 | 433 | sprintf(cmd, "echo '%.*s' >> ~/log", i, buf); 434 | system(cmd); 435 | } 436 | 437 | void 438 | applyrules(Client *c) 439 | { 440 | const char *class, *instance; 441 | unsigned int i; 442 | const Rule *r; 443 | Monitor *m; 444 | XClassHint ch = { NULL, NULL }; 445 | 446 | /* rule matching */ 447 | c->isfloating = 0; 448 | c->isglobal = 0; 449 | c->isnoborder = 0; 450 | c->isscratchpad = 0; 451 | c->tags = 0; 452 | XGetClassHint(dpy, c->win, &ch); 453 | class = ch.res_class ? ch.res_class : broken; 454 | instance = ch.res_name ? ch.res_name : broken; 455 | 456 | for (i = 0; i < LENGTH(rules); i++) { 457 | r = &rules[i]; 458 | // 当rule中定义了一个或多个属性时,只要有一个属性匹配,就认为匹配成功 459 | if ((r->title && strstr(c->name, r->title)) 460 | || (r->class && strstr(class, r->class)) 461 | || (r->instance && strstr(instance, r->instance))) 462 | { 463 | c->isfloating = r->isfloating; 464 | c->isglobal = r->isglobal; 465 | c->isnoborder = r->isnoborder; 466 | c->tags |= r->tags; 467 | c->bw = c->isnoborder ? 0 : borderpx; 468 | for (m = mons; m && m->num != r->monitor; m = m->next); 469 | if (m) 470 | c->mon = m; 471 | // 如果设定了floatposition 且未指定xy,设定窗口位置 472 | if (r->isfloating && c->x == 0 && c->y == 0) { 473 | switch (r->floatposition) { 474 | case 1: c->x = selmon->wx + gappo; c->y = selmon->wy + gappo; break; // 左上 475 | case 2: c->x = selmon->wx + (selmon->ww - WIDTH(c)) / 2 - gappo; c->y = selmon->wy + gappo; break; // 中上 476 | case 3: c->x = selmon->wx + selmon->ww - WIDTH(c) - gappo; c->y = selmon->wy + gappo; break; // 右上 477 | case 4: c->x = selmon->wx + gappo; c->y = selmon->wy + (selmon->wh - HEIGHT(c)) / 2; break; // 左中 478 | case 0: // 默认0,居中 479 | case 5: c->x = selmon->wx + (selmon->ww - WIDTH(c)) / 2; c->y = selmon->wy + (selmon->wh - HEIGHT(c)) / 2; break; // 中中 480 | case 6: c->x = selmon->wx + selmon->ww - WIDTH(c) - gappo; c->y = selmon->wy + (selmon->wh - HEIGHT(c)) / 2; break; // 右中 481 | case 7: c->x = selmon->wx + gappo; c->y = selmon->wy + selmon->wh - HEIGHT(c) - gappo; break; // 左下 482 | case 8: c->x = selmon->wx + (selmon->ww - WIDTH(c)) / 2; c->y = selmon->wy + selmon->wh - HEIGHT(c) - gappo; break; // 中下 483 | case 9: c->x = selmon->wx + selmon->ww - WIDTH(c) - gappo; c->y = selmon->wy + selmon->wh - HEIGHT(c) - gappo; break; // 右下 484 | } 485 | } 486 | break; // 有且只会匹配一个第一个符合的rule 487 | } 488 | } 489 | if (!strcmp(c->name, scratchpadname) || !strcmp(class, scratchpadname) || !strcmp(instance, scratchpadname)) { 490 | c->isscratchpad = 1; 491 | c->isfloating = 1; 492 | c->isglobal = 1; // scratchpad is default global 493 | } 494 | if (ch.res_class) 495 | XFree(ch.res_class); 496 | if (ch.res_name) 497 | XFree(ch.res_name); 498 | c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; 499 | } 500 | 501 | int 502 | applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) 503 | { 504 | int baseismin; 505 | Monitor *m = c->mon; 506 | 507 | /* set minimum possible */ 508 | *w = MAX(1, *w); 509 | *h = MAX(1, *h); 510 | if (interact) { 511 | if (*x > sw) 512 | *x = sw - WIDTH(c); 513 | if (*y > sh) 514 | *y = sh - HEIGHT(c); 515 | if (*x + *w + 2 * c->bw < 0) 516 | *x = 0; 517 | if (*y + *h + 2 * c->bw < 0) 518 | *y = 0; 519 | } else { 520 | if (*x >= m->wx + m->ww) 521 | *x = m->wx + m->ww - WIDTH(c); 522 | if (*y >= m->wy + m->wh) 523 | *y = m->wy + m->wh - HEIGHT(c); 524 | if (*x + *w + 2 * c->bw <= m->wx) 525 | *x = m->wx; 526 | if (*y + *h + 2 * c->bw <= m->wy) 527 | *y = m->wy; 528 | } 529 | if (*h < bh) 530 | *h = bh; 531 | if (*w < bh) 532 | *w = bh; 533 | if (c->isfloating) { 534 | /* see last two sentences in ICCCM 4.1.2.3 */ 535 | baseismin = c->basew == c->minw && c->baseh == c->minh; 536 | if (!baseismin) { /* temporarily remove base dimensions */ 537 | *w -= c->basew; 538 | *h -= c->baseh; 539 | } 540 | /* adjust for aspect limits */ 541 | if (c->mina > 0 && c->maxa > 0) { 542 | if (c->maxa < (float)*w / *h) 543 | *w = *h * c->maxa + 0.5; 544 | else if (c->mina < (float)*h / *w) 545 | *h = *w * c->mina + 0.5; 546 | } 547 | if (baseismin) { /* increment calculation requires this */ 548 | *w -= c->basew; 549 | *h -= c->baseh; 550 | } 551 | /* adjust for increment value */ 552 | if (c->incw) 553 | *w -= *w % c->incw; 554 | if (c->inch) 555 | *h -= *h % c->inch; 556 | /* restore base dimensions */ 557 | *w = MAX(*w + c->basew, c->minw); 558 | *h = MAX(*h + c->baseh, c->minh); 559 | if (c->maxw) 560 | *w = MIN(*w, c->maxw); 561 | if (c->maxh) 562 | *h = MIN(*h, c->maxh); 563 | } 564 | return *x != c->x || *y != c->y || *w != c->w || *h != c->h; 565 | } 566 | 567 | void 568 | arrange(Monitor *m) 569 | { 570 | if (m) 571 | showtag(m->stack); 572 | else for (m = mons; m; m = m->next) 573 | showtag(m->stack); 574 | if (m) { 575 | arrangemon(m); 576 | restack(m); 577 | } else for (m = mons; m; m = m->next) 578 | arrangemon(m); 579 | } 580 | 581 | void 582 | arrangemon(Monitor *m) 583 | { 584 | strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); 585 | m->lt[m->sellt]->arrange(m); 586 | } 587 | 588 | void 589 | attach(Client *c) 590 | { 591 | if (!newclientathead) { 592 | Client **tc; 593 | for (tc = &c->mon->clients; *tc; tc = &(*tc)->next); 594 | *tc = c; 595 | c->next = NULL; 596 | } else { 597 | c->next = c->mon->clients; 598 | c->mon->clients = c; 599 | } 600 | } 601 | 602 | void 603 | attachstack(Client *c) 604 | { 605 | c->snext = c->mon->stack; 606 | c->mon->stack = c; 607 | } 608 | 609 | void 610 | buttonpress(XEvent *e) 611 | { 612 | unsigned int i, x, click, occ = 0; 613 | Arg arg = {0}; 614 | Monitor *m; 615 | XButtonPressedEvent *ev = &e->xbutton; 616 | Client *c = wintoclient(ev->window); 617 | 618 | // 判断鼠标点击的位置 619 | click = ClkRootWin; 620 | /* focus monitor if necessary */ 621 | if ((m = wintomon(ev->window)) && m != selmon) { 622 | unfocus(selmon->sel, 1); 623 | selmon = m; 624 | focus(NULL); 625 | } 626 | int status_w = drawstatusbar(selmon, bh, stext); 627 | int system_w = getsystraywidth(); 628 | if (ev->window == selmon->barwin || (!c && selmon->showbar && (topbar ? ev->y <= selmon->wy : ev->y >= selmon->wy + selmon->wh))) { // 点击在bar上 629 | i = x = 0; 630 | blw = TEXTW(selmon->ltsymbol); 631 | 632 | for (c = m->clients; c; c = c->next) 633 | occ |= c->tags == TAGMASK ? 0 : c->tags; 634 | do { 635 | /* do not reserve space for vacant tags */ 636 | if (!(occ & 1 << i || m->tagset[m->seltags] & 1 << i)) 637 | continue; 638 | x += TEXTW(tags[i]); 639 | } while (ev->x >= x && ++i < LENGTH(tags)); 640 | if (i < LENGTH(tags)) { 641 | click = ClkTagBar; 642 | arg.ui = 1 << i; 643 | } else if (ev->x < x + blw) 644 | click = ClkLtSymbol; 645 | else if (ev->x > selmon->ww - status_w - 2 * sp - (selmon == systraytomon(selmon) ? (system_w ? system_w + systraypinning + 2 : 0) : 0)) { 646 | click = ClkStatusText; 647 | arg.i = ev->x - (selmon->ww - status_w - 2 * sp - (selmon == systraytomon(selmon) ? (system_w ? system_w + systraypinning + 2 : 0) : 0)); 648 | arg.ui = ev->button; // 1 => L,2 => M,3 => R, 5 => U, 6 => D 649 | } else { 650 | click = ClkBarEmpty; 651 | 652 | x += blw; 653 | c = m->clients; 654 | 655 | if (m->bt != 0) 656 | do { 657 | if (!ISVISIBLE(c)) 658 | continue; 659 | else 660 | x += c->taskw; 661 | } while (ev->x > x && (c = c->next)); 662 | 663 | if (c) { 664 | click = ClkWinTitle; 665 | arg.v = c; 666 | } 667 | } 668 | } else if ((c = wintoclient(ev->window))) { 669 | focus(c); 670 | restack(selmon); 671 | XAllowEvents(dpy, ReplayPointer, CurrentTime); 672 | click = ClkClientWin; 673 | } 674 | 675 | for (i = 0; i < LENGTH(buttons); i++) 676 | if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button 677 | && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) 678 | buttons[i].func((click == ClkTagBar || click == ClkWinTitle || click == ClkStatusText) && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); 679 | } 680 | 681 | void 682 | checkotherwm(void) 683 | { 684 | xerrorxlib = XSetErrorHandler(xerrorstart); 685 | /* this causes an error if some other window manager is running */ 686 | XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); 687 | XSync(dpy, False); 688 | XSetErrorHandler(xerror); 689 | XSync(dpy, False); 690 | } 691 | 692 | void 693 | cleanup(void) 694 | { 695 | Arg a = {.ui = ~0}; 696 | Layout foo = { "", NULL }; 697 | Monitor *m; 698 | size_t i; 699 | 700 | view(&a); 701 | selmon->lt[selmon->sellt] = &foo; 702 | for (m = mons; m; m = m->next) 703 | while (m->stack) 704 | unmanage(m->stack, 0); 705 | XUngrabKey(dpy, AnyKey, AnyModifier, root); 706 | while (mons) 707 | cleanupmon(mons); 708 | if (showsystray) { 709 | XUnmapWindow(dpy, systray->win); 710 | XDestroyWindow(dpy, systray->win); 711 | free(systray); 712 | } 713 | for (i = 0; i < CurLast; i++) 714 | drw_cur_free(drw, cursor[i]); 715 | for (i = 0; i < LENGTH(colors) + 1; i++) 716 | free(scheme[i]); 717 | XDestroyWindow(dpy, wmcheckwin); 718 | drw_free(drw); 719 | XSync(dpy, False); 720 | XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); 721 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); 722 | } 723 | 724 | void 725 | cleanupmon(Monitor *mon) 726 | { 727 | Monitor *m; 728 | 729 | if (mon == mons) 730 | mons = mons->next; 731 | else { 732 | for (m = mons; m && m->next != mon; m = m->next); 733 | m->next = mon->next; 734 | } 735 | XUnmapWindow(dpy, mon->barwin); 736 | XDestroyWindow(dpy, mon->barwin); 737 | free(mon); 738 | } 739 | 740 | void 741 | clientmessage(XEvent *e) 742 | { 743 | XWindowAttributes wa; 744 | XSetWindowAttributes swa; 745 | XClientMessageEvent *cme = &e->xclient; 746 | Client *c = wintoclient(cme->window); 747 | 748 | if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) { 749 | /* add systray icons */ 750 | if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { 751 | if (!(c = (Client *)calloc(1, sizeof(Client)))) 752 | die("fatal: could not malloc() %u bytes\n", sizeof(Client)); 753 | if (!(c->win = cme->data.l[2])) { 754 | free(c); 755 | return; 756 | } 757 | c->mon = selmon; 758 | c->next = systray->icons; 759 | systray->icons = c; 760 | XGetWindowAttributes(dpy, c->win, &wa); 761 | c->x = c->oldx = c->y = c->oldy = 0; 762 | c->w = c->oldw = wa.width; 763 | c->h = c->oldh = wa.height; 764 | c->oldbw = wa.border_width; 765 | c->bw = 0; 766 | c->isfloating = True; 767 | /* reuse tags field as mapped status */ 768 | c->tags = 1; 769 | updatesizehints(c); 770 | updatesystrayicongeom(c, wa.width, wa.height); 771 | XAddToSaveSet(dpy, c->win); 772 | XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask); 773 | XReparentWindow(dpy, c->win, systray->win, 0, 0); 774 | XClassHint ch = {"dwmsystray", "dwmsystray"}; 775 | XSetClassHint(dpy, c->win, &ch); 776 | /* use parents background color */ 777 | swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; 778 | XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa); 779 | sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 780 | /* FIXME not sure if I have to send these events, too */ 781 | sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 782 | sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 783 | sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 784 | XSync(dpy, False); 785 | resizebarwin(selmon); 786 | updatesystray(); 787 | setclientstate(c, NormalState); 788 | } 789 | return; 790 | } 791 | if (!c) 792 | return; 793 | if (cme->message_type == netatom[NetWMState]) { 794 | if (cme->data.l[1] == netatom[NetWMFullscreen] 795 | || cme->data.l[2] == netatom[NetWMFullscreen]) 796 | setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ 797 | || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); 798 | } else if (cme->message_type == netatom[NetActiveWindow]) { 799 | if (c != selmon->sel && !c->isurgent) 800 | seturgent(c, 1); 801 | if (c == selmon->sel) return; 802 | // 若不是当前显示器 则跳转到对应显示器 803 | if (c->mon != selmon) { 804 | focusmon(&(Arg) { .i = +1 }); 805 | } 806 | // 若不是当前tag 则跳转到对应tag 807 | if (!ISVISIBLE(c)) { 808 | view(&(Arg) { .ui = c->tags }); 809 | } 810 | } 811 | } 812 | 813 | void 814 | configure(Client *c) 815 | { 816 | XConfigureEvent ce; 817 | 818 | ce.type = ConfigureNotify; 819 | ce.display = dpy; 820 | ce.event = c->win; 821 | ce.window = c->win; 822 | ce.x = c->x; 823 | ce.y = c->y; 824 | ce.width = c->w; 825 | ce.height = c->h; 826 | ce.border_width = c->bw; 827 | ce.above = None; 828 | ce.override_redirect = False; 829 | XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); 830 | } 831 | 832 | void 833 | configurenotify(XEvent *e) 834 | { 835 | Monitor *m; 836 | Client *c; 837 | XConfigureEvent *ev = &e->xconfigure; 838 | int dirty; 839 | 840 | /* TODO: updategeom handling sucks, needs to be simplified */ 841 | if (ev->window == root) { 842 | dirty = (sw != ev->width || sh != ev->height); 843 | sw = ev->width; 844 | sh = ev->height; 845 | if (updategeom() || dirty) { 846 | drw_resize(drw, sw, bh); 847 | updatebars(); 848 | for (m = mons; m; m = m->next) { 849 | for (c = m->clients; c; c = c->next) 850 | if (c->isfullscreen) 851 | resizeclient(c, m->mx, m->my, m->mw, m->mh); 852 | resizebarwin(m); 853 | } 854 | focus(NULL); 855 | arrange(NULL); 856 | } 857 | } 858 | } 859 | 860 | void 861 | configurerequest(XEvent *e) 862 | { 863 | Client *c; 864 | Monitor *m; 865 | XConfigureRequestEvent *ev = &e->xconfigurerequest; 866 | XWindowChanges wc; 867 | 868 | if ((c = wintoclient(ev->window))) { 869 | if (ev->value_mask & CWBorderWidth) 870 | c->bw = ev->border_width; 871 | else if (c->isfloating) { 872 | m = c->mon; 873 | if (ev->value_mask & CWX) { 874 | c->oldx = c->x; 875 | c->x = m->mx + ev->x; 876 | } 877 | if (ev->value_mask & CWY) { 878 | c->oldy = c->y; 879 | c->y = m->my + ev->y; 880 | } 881 | if (ev->value_mask & CWWidth) { 882 | c->oldw = c->w; 883 | c->w = ev->width; 884 | } 885 | if (ev->value_mask & CWHeight) { 886 | c->oldh = c->h; 887 | c->h = ev->height; 888 | } 889 | if ((c->x + c->w) > m->mx + m->mw && c->isfloating) 890 | c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ 891 | if ((c->y + c->h) > m->my + m->mh && c->isfloating) 892 | c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ 893 | if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) 894 | configure(c); 895 | if (ISVISIBLE(c)) 896 | XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); 897 | } else 898 | configure(c); 899 | } else { 900 | wc.x = ev->x; 901 | wc.y = ev->y; 902 | wc.width = ev->width; 903 | wc.height = ev->height; 904 | wc.border_width = ev->border_width; 905 | wc.sibling = ev->above; 906 | wc.stack_mode = ev->detail; 907 | XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); 908 | } 909 | XSync(dpy, False); 910 | } 911 | 912 | Monitor * 913 | createmon(void) 914 | { 915 | Monitor *m; 916 | unsigned int i; 917 | 918 | m = ecalloc(1, sizeof(Monitor)); 919 | m->tagset[0] = m->tagset[1] = 1; 920 | m->mfact = mfact; 921 | m->nmaster = nmaster; 922 | m->showbar = showbar; 923 | m->topbar = topbar; 924 | m->lt[0] = &layouts[0]; 925 | m->lt[1] = &layouts[1 % LENGTH(layouts)]; 926 | strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); 927 | m->pertag = ecalloc(1, sizeof(Pertag)); 928 | m->pertag->curtag = m->pertag->prevtag = 1; 929 | 930 | for (i = 0; i <= LENGTH(tags); i++) { 931 | m->pertag->nmasters[i] = m->nmaster; 932 | m->pertag->mfacts[i] = m->mfact; 933 | 934 | m->pertag->ltidxs[i][0] = m->lt[0]; 935 | m->pertag->ltidxs[i][1] = m->lt[1]; 936 | m->pertag->sellts[i] = m->sellt; 937 | 938 | m->pertag->showbars[i] = m->showbar; 939 | } 940 | 941 | return m; 942 | } 943 | 944 | void 945 | destroynotify(XEvent *e) 946 | { 947 | Client *c; 948 | XDestroyWindowEvent *ev = &e->xdestroywindow; 949 | 950 | if ((c = wintoclient(ev->window))) 951 | unmanage(c, 1); 952 | else if ((c = wintosystrayicon(ev->window))) { 953 | removesystrayicon(c); 954 | resizebarwin(selmon); 955 | updatesystray(); 956 | } 957 | } 958 | 959 | void 960 | detach(Client *c) 961 | { 962 | Client **tc; 963 | 964 | for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); 965 | *tc = c->next; 966 | } 967 | 968 | void 969 | detachstack(Client *c) 970 | { 971 | Client **tc, *t; 972 | 973 | for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); 974 | *tc = c->snext; 975 | 976 | if (c == c->mon->sel) { 977 | for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); 978 | c->mon->sel = t; 979 | } 980 | } 981 | 982 | Monitor * 983 | dirtomon(int dir) 984 | { 985 | Monitor *m = NULL; 986 | 987 | if (dir > 0) { 988 | if (!(m = selmon->next)) 989 | m = mons; 990 | } else if (selmon == mons) 991 | for (m = mons; m->next; m = m->next); 992 | else 993 | for (m = mons; m->next != selmon; m = m->next); 994 | return m; 995 | } 996 | 997 | void 998 | drawbar(Monitor *m) 999 | { 1000 | int x, empty_w; 1001 | int w = 0; 1002 | int system_w = 0, tasks_w = 0, status_w; 1003 | unsigned int i, occ = 0, n = 0, urg = 0, scm; 1004 | Client *c; 1005 | int boxw = 2; 1006 | 1007 | if (!m->showbar) 1008 | return; 1009 | 1010 | // 获取系统托盘的宽度 1011 | if(showsystray && m == systraytomon(m)) 1012 | system_w = getsystraywidth(); 1013 | 1014 | // 绘制STATUSBAR 1015 | status_w = drawstatusbar(m, bh, stext); 1016 | 1017 | // 判断tag显示数量 1018 | for (c = m->clients; c; c = c->next) { 1019 | if (ISVISIBLE(c)) 1020 | n++; 1021 | occ |= c->tags == TAGMASK ? 0 : c->tags; 1022 | if (c->isurgent) 1023 | urg |= c->tags; 1024 | } 1025 | 1026 | // 绘制TAGS 1027 | x = 0; 1028 | 1029 | for (i = 0; i < LENGTH(tags); i++) { 1030 | /* do not draw vacant tags */ 1031 | if (!(occ & 1 << i || m->tagset[m->seltags] & 1 << i)) 1032 | continue; 1033 | 1034 | w = TEXTW(tags[i]); 1035 | drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSelTag : SchemeNormTag]); 1036 | drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); 1037 | if (m->tagset[m->seltags] & 1 << i) { 1038 | drw_setscheme(drw, scheme[SchemeUnderline]); 1039 | drw_rect(drw, x + 2, bh - boxw, w + lrpad - 4, boxw, 1, 0); 1040 | } 1041 | x += w; 1042 | } 1043 | 1044 | // 绘制模式图标 1045 | w = TEXTW(m->ltsymbol); 1046 | drw_setscheme(drw, scheme[SchemeNorm]); 1047 | x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); 1048 | 1049 | // 绘制TASKS 1050 | for (c = m->clients; c; c = c->next) { 1051 | // 判断是否需要绘制 && 判断颜色设置 1052 | if (!ISVISIBLE(c)) 1053 | continue; 1054 | if (m->sel == c) 1055 | scm = SchemeSel; 1056 | else if (HIDDEN(c)) 1057 | scm = SchemeHid; 1058 | else 1059 | scm = SchemeNorm; 1060 | drw_setscheme(drw, scheme[scm]); 1061 | 1062 | // 绘制TASK 1063 | w = MIN(TEXTW(c->name), TEXTW(" ")); 1064 | empty_w = m->ww - x - status_w - system_w; 1065 | if (w > empty_w) { // 如果当前TASK绘制后长度超过最大宽度 1066 | w = empty_w; 1067 | x = drw_text(drw, x, 0, w, bh, lrpad / 2, "...", 0); 1068 | c->taskw = w; 1069 | tasks_w += w; 1070 | break; 1071 | } else { 1072 | x = drw_text(drw, x, 0, w, bh, lrpad / 2, c->name, 0); 1073 | c->taskw = w; 1074 | tasks_w += w; 1075 | } 1076 | } 1077 | /** 空白部分的宽度 = 总宽度 - 状态栏的宽度 - 托盘的宽度 - sp (托盘存在时 额外多-一个 systrayspadding) */ 1078 | empty_w = m->ww - x - status_w - system_w - 2 * sp - (system_w ? systrayspadding : 0); 1079 | if (empty_w > 0) { 1080 | drw_setscheme(drw, scheme[SchemeBarEmpty]); 1081 | drw_rect(drw, x, 0, empty_w, bh, 1, 1); 1082 | } 1083 | 1084 | m->bt = n; 1085 | drw_map(drw, m->barwin, 0, 0, m->ww - system_w, bh); 1086 | 1087 | resizebarwin(m); 1088 | } 1089 | 1090 | void 1091 | drawbars(void) 1092 | { 1093 | Monitor *m; 1094 | 1095 | for (m = mons; m; m = m->next) 1096 | drawbar(m); 1097 | } 1098 | 1099 | int 1100 | drawstatusbar(Monitor *m, int bh, char* stext) { 1101 | int status_w = 0, i, w, x, len, system_w = 0; 1102 | short isCode = 0; 1103 | char *text; 1104 | char *p; 1105 | char buf8[8], buf5[5]; 1106 | uint textsalpha; 1107 | 1108 | if(showsystray && m == systraytomon(m)) 1109 | system_w = getsystraywidth(); 1110 | 1111 | len = strlen(stext) + 1 ; 1112 | if (!(text = (char*) malloc(sizeof(char)*len))) 1113 | die("malloc"); 1114 | p = text; 1115 | memcpy(text, stext, len); 1116 | 1117 | /* compute width of the status text */ 1118 | w = 0; 1119 | i = -1; 1120 | while (text[++i]) { 1121 | if (text[i] == '^') { 1122 | if (!isCode) { 1123 | isCode = 1; 1124 | text[i] = '\0'; 1125 | w += TEXTW(text) - lrpad; 1126 | text[i] = '^'; 1127 | } else { 1128 | isCode = 0; 1129 | text = text + i + 1; 1130 | i = -1; 1131 | } 1132 | } 1133 | } 1134 | if (!isCode) 1135 | w += TEXTW(text) - lrpad; 1136 | else 1137 | isCode = 0; 1138 | text = p; 1139 | 1140 | // w += 2; /* 1px padding on both sides */ 1141 | x = m->ww - w - system_w - 2 * sp - (system_w ? systrayspadding : 0); // 托盘存在时 额外多-一个systrayspadding 1142 | 1143 | drw_setscheme(drw, scheme[LENGTH(colors)]); 1144 | drw->scheme[ColFg] = scheme[SchemeNorm][ColFg]; 1145 | drw->scheme[ColBg] = scheme[SchemeNorm][ColBg]; 1146 | drw_rect(drw, x, 0, w, bh, 1, 1); 1147 | x++; 1148 | 1149 | /* process status text */ 1150 | i = -1; 1151 | while (text[++i]) { 1152 | if (text[i] == '^' && !isCode) { 1153 | isCode = 1; 1154 | 1155 | text[i] = '\0'; 1156 | w = TEXTW(text) - lrpad; 1157 | drw_text(drw, x, 0, w, bh, 0, text, 0); 1158 | status_w += w; 1159 | 1160 | x += w; 1161 | 1162 | /* process code */ 1163 | while (text[++i] != '^') { 1164 | if (text[i] == 'c') { 1165 | memcpy(buf8, (char*)text+i+1, 7); 1166 | buf8[7] = '\0'; 1167 | i += 7; 1168 | 1169 | textsalpha = alphas[SchemeStatusText][ColFg]; 1170 | if (text[i + 1] != '^') { 1171 | memcpy(buf5, (char*)text+i+1, 4); 1172 | buf5[4] = '\0'; 1173 | i += 4; 1174 | sscanf(buf5, "%x", &textsalpha); 1175 | } 1176 | 1177 | drw_clr_create(drw, &drw->scheme[ColFg], buf8, textsalpha); 1178 | } else if (text[i] == 'b') { 1179 | memcpy(buf8, (char*)text+i+1, 7); 1180 | buf8[7] = '\0'; 1181 | i += 7; 1182 | 1183 | textsalpha = alphas[SchemeStatusText][ColBg]; 1184 | if (text[i + 1] != '^') { 1185 | memcpy(buf5, (char*)text+i+1, 4); 1186 | buf5[4] = '\0'; 1187 | i += 4; 1188 | sscanf(buf5, "%x", &textsalpha); 1189 | } 1190 | drw_clr_create(drw, &drw->scheme[ColBg], buf8, textsalpha); 1191 | } else if (text[i] == 's') { 1192 | while (text[i + 1] != '^') i++; 1193 | } else if (text[i] == 'd') { 1194 | drw->scheme[ColFg] = scheme[SchemeNorm][ColFg]; 1195 | drw->scheme[ColBg] = scheme[SchemeNorm][ColBg]; 1196 | } 1197 | } 1198 | 1199 | text = text + i + 1; 1200 | i=-1; 1201 | isCode = 0; 1202 | } 1203 | } 1204 | 1205 | if (!isCode) { 1206 | w = TEXTW(text) - lrpad; 1207 | drw_text(drw, x, 0, w, bh, 0, text, 0); 1208 | status_w += w; 1209 | } 1210 | 1211 | drw_setscheme(drw, scheme[SchemeNorm]); 1212 | free(p); 1213 | 1214 | return status_w - 2; 1215 | } 1216 | 1217 | // 点击状态栏时执行的func 1218 | // 传入参数为 i => 鼠标点击的位置相对于左边界的距离 1219 | // 传入参数为 ui => 鼠标按键, 1 => 左键, 2 => 中键, 3 => 右键, 4 => 滚轮向上, 5 => 滚轮向下 1220 | long lastclickstatusbartime = 0; // 用于避免过于频繁的点击和滚轮行为 1221 | void 1222 | clickstatusbar(const Arg *arg) 1223 | { 1224 | if (!arg->i && arg->i < 0) 1225 | return; 1226 | 1227 | // 用于避免过于频繁的点击和滚轮行为 1228 | struct timeval tv; 1229 | gettimeofday(&tv, NULL); 1230 | long now = tv.tv_sec * 1000 + tv.tv_usec / 1000; 1231 | if (now - lastclickstatusbartime < 100) 1232 | return; 1233 | lastclickstatusbartime = now; 1234 | 1235 | int offset = -1; 1236 | int status_w = 0; 1237 | int iscode = 0, issignal = 0, signalindex = 0; 1238 | char signal [20]; 1239 | char text [100]; 1240 | char *button = "L"; 1241 | int limit, max = sizeof(stext); 1242 | 1243 | while (stext[++offset] != '\0') { 1244 | // 左侧^ 1245 | if (stext[offset] == '^' && !iscode) { 1246 | iscode = 1; 1247 | offset++; 1248 | if (stext[offset] == 's') { // 查询到s->signal 1249 | issignal = 1; 1250 | signalindex = 0; 1251 | memset(signal, '\0', sizeof(signal)); 1252 | } else { 1253 | issignal = 0; 1254 | } 1255 | continue; 1256 | } 1257 | 1258 | // 右侧^ 1259 | if (stext[offset] == '^' && iscode) { 1260 | iscode = 0; 1261 | issignal = 0; 1262 | continue; 1263 | } 1264 | 1265 | if (issignal) { // 逐位读取signal 1266 | signal[signalindex++] = stext[offset]; 1267 | } 1268 | 1269 | // 是普通文本 1270 | if (!iscode) { 1271 | // 查找到下一个^ 或 游标到达最后 1272 | limit = 0; 1273 | while (stext[offset + ++limit] != '^' && offset + limit < max); 1274 | if (offset + limit == max) 1275 | break; 1276 | 1277 | memset(text, '\0', sizeof(text)); 1278 | strncpy(text, stext + offset, limit); 1279 | offset += --limit; 1280 | status_w += TEXTW(text) - lrpad; 1281 | if (status_w > arg->i) 1282 | break; 1283 | } 1284 | } 1285 | 1286 | switch (arg->ui) { 1287 | case Button1: button = "L"; break; 1288 | case Button2: button = "M"; break; 1289 | case Button3: button = "R"; break; 1290 | case Button4: button = "U"; break; 1291 | case Button5: button = "D"; break; 1292 | } 1293 | 1294 | memset(text, '\0', sizeof(text)); 1295 | sprintf(text, "%s %s %s &", statusbarscript, signal, button); 1296 | system(text); 1297 | } 1298 | 1299 | void 1300 | enternotify(XEvent *e) 1301 | { 1302 | Client *c; 1303 | Monitor *m; 1304 | XCrossingEvent *ev = &e->xcrossing; 1305 | 1306 | if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) 1307 | return; 1308 | c = wintoclient(ev->window); 1309 | m = c ? c->mon : wintomon(ev->window); 1310 | if (m != selmon) { 1311 | unfocus(selmon->sel, 1); 1312 | selmon = m; 1313 | } else if (!c || c == selmon->sel) 1314 | return; 1315 | focus(c); 1316 | } 1317 | 1318 | void 1319 | expose(XEvent *e) 1320 | { 1321 | Monitor *m; 1322 | XExposeEvent *ev = &e->xexpose; 1323 | 1324 | if (ev->count == 0 && (m = wintomon(ev->window))) { 1325 | drawbar(m); 1326 | if (m == selmon) 1327 | updatesystray(); 1328 | } 1329 | } 1330 | 1331 | void 1332 | focus(Client *c) 1333 | { 1334 | if (!c || !ISVISIBLE(c) || HIDDEN(c)) 1335 | for (c = selmon->stack; c && (!ISVISIBLE(c) || HIDDEN(c)); c = c->snext); 1336 | if (selmon->sel && selmon->sel != c) 1337 | unfocus(selmon->sel, 0); 1338 | if (c) { 1339 | if (c->mon != selmon) 1340 | selmon = c->mon; 1341 | if (c->isurgent) 1342 | seturgent(c, 0); 1343 | detachstack(c); 1344 | attachstack(c); 1345 | grabbuttons(c, 1); 1346 | XSetWindowBorder(dpy, c->win, scheme[c->isglobal ? SchemeSelGlobal : SchemeSel][ColBorder].pixel); 1347 | setfocus(c); 1348 | } else { 1349 | XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); 1350 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); 1351 | } 1352 | selmon->sel = c; 1353 | drawbars(); 1354 | } 1355 | 1356 | /* there are some broken focus acquiring clients needing extra handling */ 1357 | void 1358 | focusin(XEvent *e) 1359 | { 1360 | XFocusChangeEvent *ev = &e->xfocus; 1361 | 1362 | if (selmon->sel && ev->window != selmon->sel->win) 1363 | setfocus(selmon->sel); 1364 | } 1365 | 1366 | void 1367 | focusmon(const Arg *arg) 1368 | { 1369 | Monitor *m; 1370 | 1371 | if (!mons->next) 1372 | return; 1373 | if ((m = dirtomon(arg->i)) == selmon) 1374 | return; 1375 | unfocus(selmon->sel, 0); 1376 | selmon = m; 1377 | focus(NULL); 1378 | pointerclient(NULL); 1379 | } 1380 | 1381 | void 1382 | focusstack(const Arg *arg) 1383 | { 1384 | Client *tempClients[100]; 1385 | Client *c = NULL, *tc = selmon->sel; 1386 | int last = -1, cur = 0, issingle = issinglewin(NULL); 1387 | 1388 | if (!tc) 1389 | tc = selmon->clients; 1390 | if (!tc) 1391 | return; 1392 | 1393 | for (c = selmon->clients; c; c = c->next) { 1394 | if (ISVISIBLE(c) && (issingle || !HIDDEN(c))) { 1395 | last ++; 1396 | tempClients[last] = c; 1397 | if (c == tc) cur = last; 1398 | } 1399 | } 1400 | if (last < 0) return; 1401 | 1402 | if (arg && arg->i == -1) { 1403 | if (cur - 1 >= 0) c = tempClients[cur - 1]; 1404 | else c = tempClients[last]; 1405 | } else { 1406 | if (cur + 1 <= last) c = tempClients[cur + 1]; 1407 | else c = tempClients[0]; 1408 | } 1409 | 1410 | if (issingle) { 1411 | if (c) 1412 | hideotherwins(&(Arg) { .v = c }); 1413 | } else { 1414 | if (c) { 1415 | pointerclient(c); 1416 | restack(selmon); 1417 | } 1418 | } 1419 | } 1420 | 1421 | void 1422 | pointerclient(Client *c) 1423 | { 1424 | if (c) { 1425 | XWarpPointer(dpy, None, root, 0, 0, 0, 0, c->x + c->w / 2, c->y + c->h / 2); 1426 | focus(c); 1427 | } else 1428 | XWarpPointer(dpy, None, root, 0, 0, 0, 0, selmon->wx + selmon->ww / 3, selmon->wy + selmon->wh / 2); 1429 | } 1430 | 1431 | Atom 1432 | getatomprop(Client *c, Atom prop) 1433 | { 1434 | int di; 1435 | unsigned long dl; 1436 | unsigned char *p = NULL; 1437 | Atom da, atom = None; 1438 | /* FIXME getatomprop should return the number of items and a pointer to 1439 | * the stored data instead of this workaround */ 1440 | Atom req = XA_ATOM; 1441 | if (prop == xatom[XembedInfo]) 1442 | req = xatom[XembedInfo]; 1443 | 1444 | if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, 1445 | &da, &di, &dl, &dl, &p) == Success && p) { 1446 | atom = *(Atom *)p; 1447 | if (da == xatom[XembedInfo] && dl == 2) 1448 | atom = ((Atom *)p)[1]; 1449 | XFree(p); 1450 | } 1451 | return atom; 1452 | } 1453 | 1454 | int 1455 | getrootptr(int *x, int *y) 1456 | { 1457 | int di; 1458 | unsigned int dui; 1459 | Window dummy; 1460 | 1461 | return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); 1462 | } 1463 | 1464 | long 1465 | getstate(Window w) 1466 | { 1467 | int format; 1468 | long result = -1; 1469 | unsigned char *p = NULL; 1470 | unsigned long n, extra; 1471 | Atom real; 1472 | 1473 | if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], 1474 | &real, &format, &n, &extra, (unsigned char **)&p) != Success) 1475 | return -1; 1476 | if (n != 0) 1477 | result = *p; 1478 | XFree(p); 1479 | return result; 1480 | } 1481 | 1482 | unsigned int 1483 | getsystraywidth() 1484 | { 1485 | unsigned int w = 0; 1486 | Client *i; 1487 | if(showsystray) 1488 | for(i = systray->icons; i; w += MAX(i->w, bh) + systrayspacing, i = i->next) ; 1489 | return w ? w + systrayspacing : 0; 1490 | } 1491 | 1492 | int 1493 | gettextprop(Window w, Atom atom, char *text, unsigned int size) 1494 | { 1495 | char **list = NULL; 1496 | int n; 1497 | XTextProperty name; 1498 | 1499 | if (!text || size == 0) 1500 | return 0; 1501 | text[0] = '\0'; 1502 | if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) 1503 | return 0; 1504 | if (name.encoding == XA_STRING) 1505 | strncpy(text, (char *)name.value, size - 1); 1506 | else { 1507 | if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { 1508 | strncpy(text, *list, size - 1); 1509 | XFreeStringList(list); 1510 | } 1511 | } 1512 | text[size - 1] = '\0'; 1513 | XFree(name.value); 1514 | return 1; 1515 | } 1516 | 1517 | void 1518 | grabbuttons(Client *c, int focused) 1519 | { 1520 | updatenumlockmask(); 1521 | { 1522 | unsigned int i, j; 1523 | unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; 1524 | XUngrabButton(dpy, AnyButton, AnyModifier, c->win); 1525 | if (!focused) 1526 | XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, 1527 | BUTTONMASK, GrabModeSync, GrabModeSync, None, None); 1528 | for (i = 0; i < LENGTH(buttons); i++) 1529 | if (buttons[i].click == ClkClientWin) 1530 | for (j = 0; j < LENGTH(modifiers); j++) 1531 | XGrabButton(dpy, buttons[i].button, 1532 | buttons[i].mask | modifiers[j], 1533 | c->win, False, BUTTONMASK, 1534 | GrabModeAsync, GrabModeSync, None, None); 1535 | } 1536 | } 1537 | 1538 | void 1539 | grabkeys(void) 1540 | { 1541 | updatenumlockmask(); 1542 | { 1543 | unsigned int i, j; 1544 | unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; 1545 | KeyCode code; 1546 | 1547 | XUngrabKey(dpy, AnyKey, AnyModifier, root); 1548 | for (i = 0; i < LENGTH(keys); i++) 1549 | if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) 1550 | for (j = 0; j < LENGTH(modifiers); j++) 1551 | XGrabKey(dpy, code, keys[i].mod | modifiers[j], root, 1552 | True, GrabModeAsync, GrabModeAsync); 1553 | } 1554 | } 1555 | 1556 | void 1557 | hide(Client *c) { 1558 | if (!c || HIDDEN(c)) 1559 | return; 1560 | 1561 | Window w = c->win; 1562 | static XWindowAttributes ra, ca; 1563 | 1564 | // more or less taken directly from blackbox's hide() function 1565 | XGrabServer(dpy); 1566 | XGetWindowAttributes(dpy, root, &ra); 1567 | XGetWindowAttributes(dpy, w, &ca); 1568 | // prevent UnmapNotify events 1569 | XSelectInput(dpy, root, ra.your_event_mask & ~SubstructureNotifyMask); 1570 | XSelectInput(dpy, w, ca.your_event_mask & ~StructureNotifyMask); 1571 | XUnmapWindow(dpy, w); 1572 | setclientstate(c, IconicState); 1573 | XSelectInput(dpy, root, ra.your_event_mask); 1574 | XSelectInput(dpy, w, ca.your_event_mask); 1575 | XUngrabServer(dpy); 1576 | 1577 | hiddenWinStack[++hiddenWinStackTop] = c; 1578 | focus(c->snext); 1579 | arrange(c->mon); 1580 | } 1581 | 1582 | void 1583 | hideotherwins(const Arg *arg) { 1584 | Client *c = (Client*)arg->v, *tc = NULL; 1585 | for (tc = selmon->clients; tc; tc = tc->next) 1586 | if (tc != c && ISVISIBLE(tc)) 1587 | hide(tc); 1588 | show(c); 1589 | focus(c); 1590 | } 1591 | 1592 | void 1593 | showonlyorall(const Arg *arg) { 1594 | Client *c; 1595 | if (issinglewin(NULL) || !selmon->sel) { 1596 | for (c = selmon->clients; c; c = c->next) 1597 | if (ISVISIBLE(c)) 1598 | show(c); 1599 | } else 1600 | hideotherwins(&(Arg) { .v = selmon->sel }); 1601 | } 1602 | 1603 | 1604 | void 1605 | incnmaster(const Arg *arg) 1606 | { 1607 | int nmaster = selmon->nmaster + arg->i; 1608 | if (selmon->bt <= 1) 1609 | nmaster = 1; 1610 | else if (nmaster >= 3) 1611 | nmaster = 1; 1612 | selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(nmaster, 1); 1613 | arrange(selmon); 1614 | } 1615 | 1616 | #ifdef XINERAMA 1617 | static int 1618 | isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) 1619 | { 1620 | while (n--) 1621 | if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org 1622 | && unique[n].width == info->width && unique[n].height == info->height) 1623 | return 0; 1624 | return 1; 1625 | } 1626 | #endif /* XINERAMA */ 1627 | 1628 | void 1629 | keypress(XEvent *e) 1630 | { 1631 | unsigned int i; 1632 | KeySym keysym; 1633 | XKeyEvent *ev; 1634 | 1635 | ev = &e->xkey; 1636 | keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); 1637 | for (i = 0; i < LENGTH(keys); i++) 1638 | if (keysym == keys[i].keysym 1639 | && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) 1640 | && keys[i].func) 1641 | keys[i].func(&(keys[i].arg)); 1642 | } 1643 | 1644 | void 1645 | killclient(const Arg *arg) 1646 | { 1647 | Client *c; 1648 | int n = 0; 1649 | 1650 | if (!selmon->sel) 1651 | return; 1652 | if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) { 1653 | XGrabServer(dpy); 1654 | XSetErrorHandler(xerrordummy); 1655 | XSetCloseDownMode(dpy, DestroyAll); 1656 | XKillClient(dpy, selmon->sel->win); 1657 | XSync(dpy, False); 1658 | XSetErrorHandler(xerror); 1659 | XUngrabServer(dpy); 1660 | } 1661 | for (c = selmon->clients; c; c = c->next) 1662 | if (ISVISIBLE(c) && !HIDDEN(c)) 1663 | n++; 1664 | if (n <= 1) 1665 | focusstack(NULL); 1666 | } 1667 | 1668 | void 1669 | forcekillclient(const Arg *arg) 1670 | { 1671 | if (!selmon->sel) 1672 | return; 1673 | killclient(arg); 1674 | unmanage(selmon->sel, 1); 1675 | } 1676 | 1677 | 1678 | void 1679 | managefloating(Client *c) 1680 | { 1681 | Client *tc; 1682 | int d1 = 0, d2 = 0, tx, ty; 1683 | int tryed = 0; 1684 | while (tryed++ < 10) { 1685 | int dw, dh, existed = 0; 1686 | dw = (selmon->ww / 20) * d1, dh = (selmon->wh / 20) * d2; 1687 | tx = c->x + dw, ty = c->y + dh; 1688 | for (tc = selmon->clients; tc; tc = tc->next) { 1689 | if (ISVISIBLE(tc) && !HIDDEN(tc) && tc != c && tc->x == tx && tc->y == ty) { 1690 | existed = 1; 1691 | break; 1692 | } 1693 | } 1694 | if (!existed) { 1695 | c->x = tx; 1696 | c->y = ty; 1697 | break; 1698 | } else { 1699 | while (d1 == 0) d1 = rand()%7 - 3; 1700 | while (d2 == 0) d2 = rand()%7 - 3; 1701 | } 1702 | } 1703 | } 1704 | 1705 | void 1706 | manage(Window w, XWindowAttributes *wa) 1707 | { 1708 | Client *c, *t = NULL; 1709 | Window trans = None; 1710 | XWindowChanges wc; 1711 | 1712 | c = ecalloc(1, sizeof(Client)); 1713 | c->win = w; 1714 | /* geometry */ 1715 | c->x = c->oldx = wa->x; 1716 | c->y = c->oldy = wa->y; 1717 | c->w = c->oldw = wa->width; 1718 | c->h = c->oldh = wa->height; 1719 | c->oldbw = wa->border_width; 1720 | c->bw = borderpx; 1721 | 1722 | updatetitle(c); 1723 | // managetransientwin 管理transient window,此时transient window也会调用applyrules 1724 | if (!managetransientwin && XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { 1725 | c->mon = t->mon; 1726 | c->tags = t->tags; 1727 | } else { 1728 | c->mon = selmon; 1729 | applyrules(c); 1730 | } 1731 | wc.border_width = c->bw; 1732 | 1733 | if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw) 1734 | c->x = c->mon->mx + c->mon->mw - WIDTH(c); 1735 | if (c->y + HEIGHT(c) > c->mon->my + c->mon->mh) 1736 | c->y = c->mon->my + c->mon->mh - HEIGHT(c); 1737 | c->x = MAX(c->x, c->mon->mx); 1738 | /* only fix client y-offset, if the client center might cover the bar */ 1739 | c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) 1740 | && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); 1741 | 1742 | if (c->isfloating) { 1743 | // if new client is floating, then manage it as floating 1744 | if (c->x==0 && c->y==0) { 1745 | c->x = selmon->wx + (selmon->ww - c->w) / 2; 1746 | c->y = selmon->wy + (selmon->wh - c->h) / 2; 1747 | } 1748 | managefloating(c); 1749 | } else { 1750 | // if new client is tile, old sel is fullscreen, then close fullscreen 1751 | if (c->mon->sel && c->mon->sel->isfullscreen) 1752 | fullscreen(NULL); 1753 | } 1754 | 1755 | XConfigureWindow(dpy, w, CWBorderWidth, &wc); 1756 | XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); 1757 | configure(c); /* propagates border_width, if size doesn't change */ 1758 | updatewindowtype(c); 1759 | updatesizehints(c); 1760 | updatewmhints(c); 1761 | XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); 1762 | grabbuttons(c, 0); 1763 | if (!c->isfloating) 1764 | c->isfloating = c->oldstate = trans != None || c->isfixed; 1765 | if (c->isfloating) 1766 | XRaiseWindow(dpy, c->win); 1767 | attach(c); 1768 | attachstack(c); 1769 | XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, 1770 | (unsigned char *) &(c->win), 1); 1771 | XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ 1772 | if (!HIDDEN(c)) 1773 | setclientstate(c, NormalState); 1774 | if (c->mon == selmon) 1775 | unfocus(selmon->sel, 0); 1776 | c->mon->sel = c; 1777 | arrange(c->mon); 1778 | if (!HIDDEN(c)) 1779 | XMapWindow(dpy, c->win); 1780 | focus(NULL); 1781 | } 1782 | 1783 | void 1784 | mappingnotify(XEvent *e) 1785 | { 1786 | XMappingEvent *ev = &e->xmapping; 1787 | 1788 | XRefreshKeyboardMapping(ev); 1789 | if (ev->request == MappingKeyboard) 1790 | grabkeys(); 1791 | } 1792 | 1793 | void 1794 | maprequest(XEvent *e) 1795 | { 1796 | static XWindowAttributes wa; 1797 | XMapRequestEvent *ev = &e->xmaprequest; 1798 | Client *i; 1799 | if ((i = wintosystrayicon(ev->window))) { 1800 | sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); 1801 | resizebarwin(selmon); 1802 | updatesystray(); 1803 | } 1804 | 1805 | if (!XGetWindowAttributes(dpy, ev->window, &wa)) 1806 | return; 1807 | if (wa.override_redirect) 1808 | return; 1809 | if (!wintoclient(ev->window)) 1810 | manage(ev->window, &wa); 1811 | } 1812 | 1813 | void 1814 | motionnotify(XEvent *e) 1815 | { 1816 | static Monitor *mon = NULL; 1817 | Monitor *m; 1818 | XMotionEvent *ev = &e->xmotion; 1819 | 1820 | if (ev->window != root) 1821 | return; 1822 | if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { 1823 | unfocus(selmon->sel, 1); 1824 | selmon = m; 1825 | focus(NULL); 1826 | } 1827 | mon = m; 1828 | } 1829 | 1830 | void 1831 | movemouse(const Arg *arg) 1832 | { 1833 | int x, y, ocx, ocy, nx, ny; 1834 | Client *c; 1835 | Monitor *m; 1836 | XEvent ev; 1837 | Time lasttime = 0; 1838 | 1839 | if (!(c = selmon->sel)) 1840 | return; 1841 | if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ 1842 | return; 1843 | restack(selmon); 1844 | ocx = c->x; 1845 | ocy = c->y; 1846 | if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, 1847 | None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) 1848 | return; 1849 | if (!getrootptr(&x, &y)) 1850 | return; 1851 | do { 1852 | XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); 1853 | switch(ev.type) { 1854 | case ConfigureRequest: 1855 | case Expose: 1856 | case MapRequest: 1857 | handler[ev.type](&ev); 1858 | break; 1859 | case MotionNotify: 1860 | if ((ev.xmotion.time - lasttime) <= (1000 / 120)) 1861 | continue; 1862 | lasttime = ev.xmotion.time; 1863 | 1864 | nx = ocx + (ev.xmotion.x - x); 1865 | ny = ocy + (ev.xmotion.y - y); 1866 | if (abs(selmon->wx - nx) < snap) 1867 | nx = selmon->wx; 1868 | else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) 1869 | nx = selmon->wx + selmon->ww - WIDTH(c); 1870 | if (abs(selmon->wy - ny) < snap) 1871 | ny = selmon->wy; 1872 | else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) 1873 | ny = selmon->wy + selmon->wh - HEIGHT(c); 1874 | if (!c->isfloating && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) { 1875 | c->isfloating = 1; 1876 | arrange(selmon); 1877 | if (ev.xmotion.x - nx < c->w / 2 && ev.xmotion.y - ny < c->h / 2 && (c->w > selmon->ww * 0.5 || c->h > selmon->wh * 0.5)) { 1878 | resize(c, nx, ny, c->w > selmon->ww * 0.5 ? c->w / 2 : c->w, c->h > selmon->wh * 0.5 ? c->h / 2 : c->h, 0); 1879 | break; 1880 | } 1881 | } 1882 | if (c->isfloating) 1883 | resize(c, nx, ny, c->w, c->h, 1); 1884 | break; 1885 | } 1886 | } while (ev.type != ButtonRelease); 1887 | XUngrabPointer(dpy, CurrentTime); 1888 | if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { 1889 | sendmon(c, m); 1890 | selmon = m; 1891 | focus(NULL); 1892 | } 1893 | } 1894 | 1895 | void 1896 | movewin(const Arg *arg) 1897 | { 1898 | Client *c, *tc; 1899 | int nx, ny; 1900 | int buttom, top, left, right, tar; 1901 | c = selmon->sel; 1902 | if (!c || c->isfullscreen) 1903 | return; 1904 | if (!c->isfloating) 1905 | togglefloating(NULL); 1906 | nx = c->x; 1907 | ny = c->y; 1908 | switch (arg->ui) { 1909 | case UP: 1910 | tar = -99999; 1911 | top = c->y; 1912 | ny -= c->mon->wh / 4; 1913 | for (tc = c->mon->clients; tc; tc = tc->next) { 1914 | // 若浮动tc c的顶边会穿过tc的底边 1915 | if (!ISVISIBLE(tc) || !tc->isfloating || tc == c) continue; 1916 | if (c->x + WIDTH(c) < tc->x || c->x > tc->x + WIDTH(tc)) continue; 1917 | buttom = tc->y + HEIGHT(tc) + gappi; 1918 | if (top > buttom && ny < buttom) { 1919 | tar = MAX(tar, buttom); 1920 | }; 1921 | } 1922 | ny = tar == -99999 ? ny : tar; 1923 | ny = MAX(ny, c->mon->wy + gappo); 1924 | break; 1925 | case DOWN: 1926 | tar = 99999; 1927 | buttom = c->y + HEIGHT(c); 1928 | ny += c->mon->wh / 4; 1929 | for (tc = c->mon->clients; tc; tc = tc->next) { 1930 | // 若浮动tc c的底边会穿过tc的顶边 1931 | if (!ISVISIBLE(tc) || !tc->isfloating || tc == c) continue; 1932 | if (c->x + WIDTH(c) < tc->x || c->x > tc->x + WIDTH(tc)) continue; 1933 | top = tc->y - gappi; 1934 | if (buttom < top && (ny + HEIGHT(c)) > top) { 1935 | tar = MIN(tar, top - HEIGHT(c)); 1936 | }; 1937 | } 1938 | ny = tar == 99999 ? ny : tar; 1939 | ny = MIN(ny, c->mon->wy + c->mon->wh - gappo - HEIGHT(c)); 1940 | break; 1941 | case LEFT: 1942 | tar = -99999; 1943 | left = c->x; 1944 | nx -= c->mon->ww / 6; 1945 | for (tc = c->mon->clients; tc; tc = tc->next) { 1946 | // 若浮动tc c的左边会穿过tc的右边 1947 | if (!ISVISIBLE(tc) || !tc->isfloating || tc == c) continue; 1948 | if (c->y + HEIGHT(c) < tc->y || c->y > tc->y + HEIGHT(tc)) continue; 1949 | right = tc->x + WIDTH(tc) + gappi; 1950 | if (left > right && nx < right) { 1951 | tar = MAX(tar, right); 1952 | }; 1953 | } 1954 | nx = tar == -99999 ? nx : tar; 1955 | nx = MAX(nx, c->mon->wx + gappo); 1956 | break; 1957 | case RIGHT: 1958 | tar = 99999; 1959 | right = c->x + WIDTH(c); 1960 | nx += c->mon->ww / 6; 1961 | for (tc = c->mon->clients; tc; tc = tc->next) { 1962 | // 若浮动tc c的右边会穿过tc的左边 1963 | if (!ISVISIBLE(tc) || !tc->isfloating || tc == c) continue; 1964 | if (c->y + HEIGHT(c) < tc->y || c->y > tc->y + HEIGHT(tc)) continue; 1965 | left = tc->x - gappi; 1966 | if (right < left && (nx + WIDTH(c)) > left) { 1967 | tar = MIN(tar, left - WIDTH(c)); 1968 | }; 1969 | } 1970 | nx = tar == 99999 ? nx : tar; 1971 | nx = MIN(nx, c->mon->wx + c->mon->ww - gappo - WIDTH(c)); 1972 | break; 1973 | } 1974 | resize(c, nx, ny, c->w, c->h, 1); 1975 | pointerclient(c); 1976 | restack(selmon); 1977 | } 1978 | 1979 | void 1980 | resizewin(const Arg *arg) 1981 | { 1982 | Client *c, *tc; 1983 | int nh, nw; 1984 | int buttom, top, left, right, tar; 1985 | c = selmon->sel; 1986 | if (!c || c->isfullscreen) 1987 | return; 1988 | if (!c->isfloating) 1989 | togglefloating(NULL); 1990 | nw = c->w; 1991 | nh = c->h; 1992 | switch (arg->ui) { 1993 | case H_EXPAND: // 右 1994 | tar = 99999; 1995 | right = c->x + WIDTH(c); 1996 | nw += selmon->ww / 16; 1997 | for (tc = c->mon->clients; tc; tc = tc->next) { 1998 | // 若浮动tc c的右边会穿过tc的左边 1999 | if (!ISVISIBLE(tc) || !tc->isfloating || tc == c) continue; 2000 | if (c->y + HEIGHT(c) < tc->y || c->y > tc->y + HEIGHT(tc)) continue; 2001 | left = tc->x - gappi; 2002 | if (right < left && (c->x + nw) > left) { 2003 | tar = MIN(tar, left - c->x - 2 * c->bw); 2004 | }; 2005 | } 2006 | nw = tar == 99999 ? nw : tar; 2007 | if (c->x + nw + gappo + 2 * c->bw > selmon->wx + selmon->ww) 2008 | nw = selmon->wx + selmon->ww - c->x - gappo - 2 * c->bw; 2009 | break; 2010 | case H_REDUCE: // 左 2011 | nw -= selmon->ww / 16; 2012 | nw = MAX(nw, selmon->ww / 10); 2013 | break; 2014 | case V_EXPAND: // 下 2015 | tar = -99999; 2016 | buttom = c->y + HEIGHT(c); 2017 | nh += selmon->wh / 8; 2018 | for (tc = c->mon->clients; tc; tc = tc->next) { 2019 | // 若浮动tc c的底边会穿过tc的顶边 2020 | if (!ISVISIBLE(tc) || !tc->isfloating || tc == c) continue; 2021 | if (c->x + WIDTH(c) < tc->x || c->x > tc->x + WIDTH(tc)) continue; 2022 | top = tc->y - gappi; 2023 | if (buttom < top && (c->y + nh) > top) { 2024 | tar = MAX(tar, top - c->y - 2 * c->bw); 2025 | }; 2026 | } 2027 | nh = tar == -99999 ? nh : tar; 2028 | if (c->y + nh + gappo + 2 * c->bw > selmon->wy + selmon->wh) 2029 | nh = selmon->wy + selmon->wh - c->y - gappo - 2 * c->bw; 2030 | break; 2031 | case V_REDUCE: // 上 2032 | nh -= selmon->wh / 8; 2033 | nh = MAX(nh, selmon->wh / 10); 2034 | break; 2035 | } 2036 | resize(c, c->x, c->y, nw, nh, 1); 2037 | XWarpPointer(dpy, None, root, 0, 0, 0, 0, c->x + c->w - 2 * c->bw, c->y + c->h - 2 * c->bw); 2038 | restack(selmon); 2039 | } 2040 | 2041 | Client * 2042 | nexttiled(Client *c) 2043 | { 2044 | for (; c && (c->isfloating || !ISVISIBLE(c) || HIDDEN(c)); c = c->next); 2045 | return c; 2046 | } 2047 | 2048 | void 2049 | pop(Client *c) 2050 | { 2051 | detach(c); 2052 | c->next = c->mon->clients; 2053 | c->mon->clients = c; 2054 | focus(c); 2055 | arrange(c->mon); 2056 | pointerclient(c); 2057 | } 2058 | 2059 | void 2060 | propertynotify(XEvent *e) 2061 | { 2062 | Client *c; 2063 | Window trans; 2064 | XPropertyEvent *ev = &e->xproperty; 2065 | 2066 | if ((c = wintosystrayicon(ev->window))) { 2067 | if (ev->atom == XA_WM_NORMAL_HINTS) { 2068 | updatesizehints(c); 2069 | updatesystrayicongeom(c, c->w, c->h); 2070 | } 2071 | else 2072 | updatesystrayiconstate(c, ev); 2073 | resizebarwin(selmon); 2074 | updatesystray(); 2075 | } 2076 | if ((ev->window == root) && (ev->atom == XA_WM_NAME)) 2077 | updatestatus(); 2078 | else if (ev->state == PropertyDelete) 2079 | return; /* ignore */ 2080 | else if ((c = wintoclient(ev->window))) { 2081 | switch(ev->atom) { 2082 | default: break; 2083 | case XA_WM_TRANSIENT_FOR: 2084 | if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && 2085 | (c->isfloating = (wintoclient(trans)) != NULL)) 2086 | arrange(c->mon); 2087 | break; 2088 | case XA_WM_NORMAL_HINTS: 2089 | updatesizehints(c); 2090 | break; 2091 | case XA_WM_HINTS: 2092 | updatewmhints(c); 2093 | drawbars(); 2094 | break; 2095 | } 2096 | if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { 2097 | updatetitle(c); 2098 | if (c == c->mon->sel) 2099 | drawbar(c->mon); 2100 | } 2101 | if (ev->atom == netatom[NetWMWindowType]) 2102 | updatewindowtype(c); 2103 | } 2104 | } 2105 | 2106 | void 2107 | quit(const Arg *arg) 2108 | { 2109 | running = 0; 2110 | } 2111 | 2112 | Monitor * 2113 | recttomon(int x, int y, int w, int h) 2114 | { 2115 | Monitor *m, *r = selmon; 2116 | int a, area = 0; 2117 | 2118 | for (m = mons; m; m = m->next) 2119 | if ((a = INTERSECT(x, y, w, h, m)) > area) { 2120 | area = a; 2121 | r = m; 2122 | } 2123 | return r; 2124 | } 2125 | 2126 | void 2127 | removesystrayicon(Client *i) 2128 | { 2129 | Client **ii; 2130 | 2131 | if (!showsystray || !i) 2132 | return; 2133 | for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next); 2134 | if (ii) 2135 | *ii = i->next; 2136 | free(i); 2137 | } 2138 | 2139 | 2140 | void 2141 | resize(Client *c, int x, int y, int w, int h, int interact) 2142 | { 2143 | if (applysizehints(c, &x, &y, &w, &h, interact)) { 2144 | resizeclient(c, x, y, w, h); 2145 | } 2146 | } 2147 | 2148 | void 2149 | resizebarwin(Monitor *m) { 2150 | unsigned int w = m->ww; 2151 | uint system_w = getsystraywidth(); 2152 | if (showsystray && m == systraytomon(m)) 2153 | w -= system_w; 2154 | XMoveResizeWindow(dpy, m->barwin, m->wx + sp, m->by + vp, w - 2 * sp - (m == systraytomon(m) && system_w ? systrayspadding : 0), bh); // 如果托盘存在 额外减去systrayspadding 2155 | } 2156 | 2157 | void 2158 | resizeclient(Client *c, int x, int y, int w, int h) 2159 | { 2160 | XWindowChanges wc; 2161 | 2162 | c->oldx = c->x; c->x = wc.x = x; 2163 | c->oldy = c->y; c->y = wc.y = y; 2164 | c->oldw = c->w; c->w = wc.width = w; 2165 | c->oldh = c->h; c->h = wc.height = h; 2166 | wc.border_width = c->bw; 2167 | 2168 | if (((nexttiled(c->mon->clients) == c && !nexttiled(c->next))) 2169 | && !c->isfullscreen && !c->isfloating) { 2170 | c->w = wc.width += c->bw * 2; 2171 | c->h = wc.height += c->bw * 2; 2172 | wc.border_width = 0; 2173 | } 2174 | XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); 2175 | configure(c); 2176 | XSync(dpy, False); 2177 | } 2178 | 2179 | void 2180 | resizemouse(const Arg *arg) 2181 | { 2182 | int ocx, ocy, nw, nh; 2183 | Client *c; 2184 | Monitor *m; 2185 | XEvent ev; 2186 | Time lasttime = 0; 2187 | 2188 | if (!(c = selmon->sel)) 2189 | return; 2190 | if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ 2191 | return; 2192 | restack(selmon); 2193 | ocx = c->x; 2194 | ocy = c->y; 2195 | if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, 2196 | None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) 2197 | return; 2198 | XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); 2199 | do { 2200 | XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); 2201 | switch(ev.type) { 2202 | case ConfigureRequest: 2203 | case Expose: 2204 | case MapRequest: 2205 | handler[ev.type](&ev); 2206 | break; 2207 | case MotionNotify: 2208 | if ((ev.xmotion.time - lasttime) <= (1000 / 120)) 2209 | continue; 2210 | lasttime = ev.xmotion.time; 2211 | 2212 | nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); 2213 | nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); 2214 | if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww 2215 | && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) { 2216 | if (!c->isfloating && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) { 2217 | c->isfloating = 1; 2218 | arrange(selmon); 2219 | } 2220 | } 2221 | if (c->isfloating) 2222 | resize(c, c->x, c->y, nw, nh, 1); 2223 | break; 2224 | } 2225 | } while (ev.type != ButtonRelease); 2226 | XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); 2227 | XUngrabPointer(dpy, CurrentTime); 2228 | while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); 2229 | if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { 2230 | sendmon(c, m); 2231 | selmon = m; 2232 | focus(NULL); 2233 | } 2234 | } 2235 | 2236 | void 2237 | resizerequest(XEvent *e) 2238 | { 2239 | XResizeRequestEvent *ev = &e->xresizerequest; 2240 | Client *i; 2241 | 2242 | if ((i = wintosystrayicon(ev->window))) { 2243 | updatesystrayicongeom(i, ev->width, ev->height); 2244 | resizebarwin(selmon); 2245 | updatesystray(); 2246 | } 2247 | } 2248 | 2249 | void 2250 | restack(Monitor *m) 2251 | { 2252 | Client *c; 2253 | XEvent ev; 2254 | XWindowChanges wc; 2255 | 2256 | drawbar(m); 2257 | if (!m->sel) 2258 | return; 2259 | if (m->sel->isfloating) 2260 | XRaiseWindow(dpy, m->sel->win); 2261 | wc.stack_mode = Below; 2262 | wc.sibling = m->barwin; 2263 | for (c = m->stack; c; c = c->snext) 2264 | if (!c->isfloating && ISVISIBLE(c)) { 2265 | XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); 2266 | wc.sibling = c->win; 2267 | } 2268 | XSync(dpy, False); 2269 | while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); 2270 | } 2271 | 2272 | void 2273 | run(void) 2274 | { 2275 | XEvent ev; 2276 | /* main event loop */ 2277 | XSync(dpy, False); 2278 | while (running && !XNextEvent(dpy, &ev)) 2279 | if (handler[ev.type]) 2280 | handler[ev.type](&ev); /* call handler */ 2281 | } 2282 | 2283 | void 2284 | runAutostart(void) { 2285 | char cmd [100]; 2286 | sprintf(cmd, "%s &", autostartscript); 2287 | system(cmd); 2288 | } 2289 | 2290 | void 2291 | scan(void) 2292 | { 2293 | unsigned int i, num; 2294 | Window d1, d2, *wins = NULL; 2295 | XWindowAttributes wa; 2296 | 2297 | if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { 2298 | for (i = 0; i < num; i++) { 2299 | if (!XGetWindowAttributes(dpy, wins[i], &wa) 2300 | || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) 2301 | continue; 2302 | if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) 2303 | manage(wins[i], &wa); 2304 | } 2305 | for (i = 0; i < num; i++) { /* now the transients */ 2306 | if (!XGetWindowAttributes(dpy, wins[i], &wa)) 2307 | continue; 2308 | if (XGetTransientForHint(dpy, wins[i], &d1) 2309 | && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) 2310 | manage(wins[i], &wa); 2311 | } 2312 | if (wins) 2313 | XFree(wins); 2314 | } 2315 | } 2316 | 2317 | void 2318 | sendmon(Client *c, Monitor *m) 2319 | { 2320 | if (c->mon == m) 2321 | return; 2322 | unfocus(c, 1); 2323 | detach(c); 2324 | detachstack(c); 2325 | c->mon = m; 2326 | c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ 2327 | attach(c); 2328 | attachstack(c); 2329 | focus(NULL); 2330 | arrange(NULL); 2331 | } 2332 | 2333 | void 2334 | setclientstate(Client *c, long state) 2335 | { 2336 | long data[] = { state, None }; 2337 | 2338 | XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, 2339 | PropModeReplace, (unsigned char *)data, 2); 2340 | } 2341 | 2342 | void 2343 | tagtoleft(const Arg *arg) { 2344 | if(selmon->sel != NULL 2345 | && __builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 2346 | && selmon->tagset[selmon->seltags] > 1) { 2347 | tag(&(Arg) { .ui = selmon->tagset[selmon->seltags] >> 1 }); 2348 | } 2349 | } 2350 | 2351 | void 2352 | tagtoright(const Arg *arg) { 2353 | if(selmon->sel != NULL 2354 | && __builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 2355 | && selmon->tagset[selmon->seltags] & (TAGMASK >> 1)) { 2356 | tag(&(Arg) { .ui = selmon->tagset[selmon->seltags] << 1 }); 2357 | } 2358 | } 2359 | 2360 | int 2361 | sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) 2362 | { 2363 | int n; 2364 | Atom *protocols, mt; 2365 | int exists = 0; 2366 | XEvent ev; 2367 | 2368 | if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { 2369 | mt = wmatom[WMProtocols]; 2370 | if (XGetWMProtocols(dpy, w, &protocols, &n)) { 2371 | while (!exists && n--) 2372 | exists = protocols[n] == proto; 2373 | XFree(protocols); 2374 | } 2375 | } 2376 | else { 2377 | exists = True; 2378 | mt = proto; 2379 | } 2380 | if (exists) { 2381 | ev.type = ClientMessage; 2382 | ev.xclient.window = w; 2383 | ev.xclient.message_type = mt; 2384 | ev.xclient.format = 32; 2385 | ev.xclient.data.l[0] = d0; 2386 | ev.xclient.data.l[1] = d1; 2387 | ev.xclient.data.l[2] = d2; 2388 | ev.xclient.data.l[3] = d3; 2389 | ev.xclient.data.l[4] = d4; 2390 | XSendEvent(dpy, w, False, mask, &ev); 2391 | } 2392 | return exists; 2393 | } 2394 | 2395 | void 2396 | setfocus(Client *c) 2397 | { 2398 | if (!c->neverfocus) { 2399 | XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); 2400 | XChangeProperty(dpy, root, netatom[NetActiveWindow], 2401 | XA_WINDOW, 32, PropModeReplace, 2402 | (unsigned char *) &(c->win), 1); 2403 | } 2404 | sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); 2405 | } 2406 | 2407 | void 2408 | setfullscreen(Client *c, int fullscreen) 2409 | { 2410 | if (fullscreen && !c->isfullscreen) { 2411 | XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, 2412 | PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); 2413 | c->isfullscreen = 1; 2414 | c->oldstate = c->isfloating; 2415 | c->oldbw = c->bw; 2416 | c->bw = 0; 2417 | c->isfloating = 1; 2418 | resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); 2419 | XRaiseWindow(dpy, c->win); 2420 | } else if (!fullscreen && c->isfullscreen){ 2421 | XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, 2422 | PropModeReplace, (unsigned char*)0, 0); 2423 | c->isfullscreen = 0; 2424 | c->isfloating = c->oldstate; 2425 | c->bw = c->oldbw; 2426 | c->x = c->oldx; 2427 | c->y = c->oldy; 2428 | c->w = c->oldw; 2429 | c->h = c->oldh; 2430 | resizeclient(c, c->x, c->y, c->w, c->h); 2431 | arrange(c->mon); 2432 | } 2433 | } 2434 | 2435 | void 2436 | fullscreen(const Arg *arg) 2437 | { 2438 | if (!selmon->sel) { 2439 | togglebar(arg); 2440 | return; 2441 | } 2442 | if (selmon->sel->isfullscreen) { 2443 | setfullscreen(selmon->sel, 0); 2444 | if (!selmon->showbar) 2445 | togglebar(arg); 2446 | } else { 2447 | setfullscreen(selmon->sel, 1); 2448 | if (selmon->showbar) 2449 | togglebar(arg); 2450 | } 2451 | } 2452 | 2453 | void 2454 | selectlayout(const Arg *arg) 2455 | { 2456 | const Layout *cur = selmon->lt[selmon->sellt]; 2457 | const Layout *target = cur == arg->v ? &layouts[0] : arg->v; 2458 | setlayout(&(Arg) { .v = target }); 2459 | } 2460 | 2461 | void 2462 | setlayout(const Arg *arg) 2463 | { 2464 | if (arg->v != selmon->lt[selmon->sellt]) 2465 | selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; 2466 | if (arg->v) 2467 | selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v; 2468 | arrange(selmon); 2469 | } 2470 | 2471 | void 2472 | setmfact(const Arg *arg) 2473 | { 2474 | float f; 2475 | 2476 | if (!arg) 2477 | return; 2478 | f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; 2479 | if (f < 0.05 || f > 0.95) 2480 | return; 2481 | selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f; 2482 | arrange(selmon); 2483 | } 2484 | 2485 | void 2486 | setup(void) 2487 | { 2488 | int i; 2489 | XSetWindowAttributes wa; 2490 | Atom utf8string; 2491 | 2492 | /* clean up any zombies immediately */ 2493 | sigchld(0); 2494 | 2495 | /* init screen */ 2496 | screen = DefaultScreen(dpy); 2497 | sw = DisplayWidth(dpy, screen); 2498 | sh = DisplayHeight(dpy, screen); 2499 | root = RootWindow(dpy, screen); 2500 | xinitvisual(); 2501 | drw = drw_create(dpy, screen, root, sw, sh, visual, depth, cmap); 2502 | if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) 2503 | die("no fonts could be loaded."); 2504 | lrpad = drw->fonts->h; 2505 | bh = drw->fonts->h + 2; 2506 | sp = sidepad; 2507 | vp = (topbar == 1) ? vertpad : - vertpad; 2508 | updategeom(); 2509 | /* init atoms */ 2510 | utf8string = XInternAtom(dpy, "UTF8_STRING", False); 2511 | wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); 2512 | wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); 2513 | wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); 2514 | wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); 2515 | netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); 2516 | netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); 2517 | netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); 2518 | netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); 2519 | netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); 2520 | netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False); 2521 | netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); 2522 | netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); 2523 | netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); 2524 | netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); 2525 | netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); 2526 | netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); 2527 | netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); 2528 | xatom[Manager] = XInternAtom(dpy, "MANAGER", False); 2529 | xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); 2530 | xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); 2531 | /* init cursors */ 2532 | cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); 2533 | cursor[CurResize] = drw_cur_create(drw, XC_sizing); 2534 | cursor[CurMove] = drw_cur_create(drw, XC_fleur); 2535 | /* init appearance */ 2536 | scheme = ecalloc(LENGTH(colors) + 1, sizeof(Clr *)); 2537 | scheme[LENGTH(colors)] = drw_scm_create(drw, colors[0], alphas[0], 3); 2538 | for (i = 0; i < LENGTH(colors); i++) 2539 | scheme[i] = drw_scm_create(drw, colors[i], alphas[i], 3); 2540 | /* init system tray */ 2541 | updatesystray(); 2542 | /* init bars */ 2543 | updatebars(); 2544 | updatestatus(); 2545 | /* supporting window for NetWMCheck */ 2546 | wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); 2547 | XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, 2548 | PropModeReplace, (unsigned char *) &wmcheckwin, 1); 2549 | XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, 2550 | PropModeReplace, (unsigned char *) "dwm", 3); 2551 | XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, 2552 | PropModeReplace, (unsigned char *) &wmcheckwin, 1); 2553 | /* EWMH support per view */ 2554 | XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, 2555 | PropModeReplace, (unsigned char *) netatom, NetLast); 2556 | XDeleteProperty(dpy, root, netatom[NetClientList]); 2557 | /* select events */ 2558 | wa.cursor = cursor[CurNormal]->cursor; 2559 | wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask 2560 | |ButtonPressMask|PointerMotionMask|EnterWindowMask 2561 | |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; 2562 | XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); 2563 | XSelectInput(dpy, root, wa.event_mask); 2564 | grabkeys(); 2565 | focus(NULL); 2566 | } 2567 | 2568 | 2569 | void 2570 | seturgent(Client *c, int urg) 2571 | { 2572 | XWMHints *wmh; 2573 | 2574 | c->isurgent = urg; 2575 | if (!(wmh = XGetWMHints(dpy, c->win))) 2576 | return; 2577 | wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); 2578 | XSetWMHints(dpy, c->win, wmh); 2579 | XFree(wmh); 2580 | } 2581 | 2582 | void 2583 | show(Client *c) 2584 | { 2585 | if (!c || !HIDDEN(c)) 2586 | return; 2587 | 2588 | XMapWindow(dpy, c->win); 2589 | setclientstate(c, NormalState); 2590 | hiddenWinStackTop--; 2591 | arrange(c->mon); 2592 | } 2593 | 2594 | // 该方法为显示当前tag下的窗口的func,切换时会将原窗口下的win放到屏幕之外 2595 | void 2596 | showtag(Client *c) 2597 | { 2598 | if (!c) return; 2599 | 2600 | if (ISVISIBLE(c)) { 2601 | XMoveWindow(dpy, c->win, c->x, c->y); 2602 | if (c->isfloating && !c->isfullscreen) resize(c, c->x, c->y, c->w, c->h, 0); 2603 | 2604 | showtag(c->snext); 2605 | } else { 2606 | showtag(c->snext); 2607 | 2608 | // 获取mon数量 2609 | int monitor_count = 0; 2610 | int maxmx = 0; 2611 | int maxmb = 0; // 最大的bottom 2612 | for (Monitor *m = mons; m; m = m->next) { 2613 | monitor_count++; 2614 | if (m->my + m->mh > maxmb) maxmb = m->my + m->mh; 2615 | if (m->mx > maxmx) maxmx = m->mx; 2616 | }; 2617 | 2618 | if (monitor_count == 1) { 2619 | // 仅单个mon时,按tag大小觉得从左边或右边显示 2620 | unsigned int only_tag = (c->tags & (c->tags - 1)) == 0; 2621 | if (only_tag && (TAGMASK & c->tags) >= 1 << c->mon->pertag->curtag) XMoveWindow(dpy, c->win, c->x + c->mon->mx + c->mon->mw, c->y); 2622 | else { 2623 | if (c->x + c->w > c->mon->mx + c->mon->mw) XMoveWindow(dpy, c->win, -1 * WIDTH(c), c->y); 2624 | else XMoveWindow(dpy, c->win, c->x + c->mon->mx - c->mon->mw, c->y); 2625 | } 2626 | } else if (monitor_count == 2) { 2627 | // 两个mon时,左边窗口的mon藏在左边,右边窗口的mon藏在右边 2628 | if (c->mon->mx == 0) { 2629 | XMoveWindow(dpy, c->win, c->mon->mw * -1, c->y); 2630 | } else { 2631 | XMoveWindow(dpy, c->win, c->mon->mx + c->mon->mw, c->y); 2632 | } 2633 | } else if (monitor_count > 2) { 2634 | // 超过2个时(假定为3个),左边窗口的mon藏在左边,右边窗口的mon藏在右边,中间窗口的mon藏在下边 2635 | if (c->mon->mx == 0) { 2636 | XMoveWindow(dpy, c->win, c->mon->mw * -1, c->y); 2637 | } else if (c->mon->mx == maxmx) { 2638 | XMoveWindow(dpy, c->win, c->mon->mx + c->mon->mw, c->y); 2639 | } else { 2640 | // 中间窗口 将窗口藏到monitor下面 2641 | // 如果client超过了边界,藏到所有monitor的下面,避免有一半出现在其他屏幕上 2642 | XMoveWindow(dpy, c->win, c->x, c->x < c->mon->mx || c->x + c->x+c->w > c->mon->mx + c->mon->mw ? maxmb : c->mon->my + c->mon->mh); 2643 | } 2644 | } 2645 | } 2646 | } 2647 | 2648 | void 2649 | sigchld(int unused) 2650 | { 2651 | if (signal(SIGCHLD, sigchld) == SIG_ERR) 2652 | die("can't install SIGCHLD handler:"); 2653 | while (0 < waitpid(-1, NULL, WNOHANG)); 2654 | } 2655 | 2656 | void 2657 | spawn(const Arg *arg) 2658 | { 2659 | if (fork() == 0) { 2660 | if (dpy) 2661 | close(ConnectionNumber(dpy)); 2662 | setsid(); 2663 | execvp(((char **)arg->v)[0], (char **)arg->v); 2664 | fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]); 2665 | perror(" failed"); 2666 | exit(EXIT_SUCCESS); 2667 | } 2668 | } 2669 | 2670 | void 2671 | tag(const Arg *arg) 2672 | { 2673 | if (selmon->sel && !selmon->sel->isglobal && arg->ui & TAGMASK) { 2674 | selmon->sel->tags = arg->ui & TAGMASK; 2675 | focus(NULL); 2676 | arrange(selmon); 2677 | view(&(Arg) { .ui = arg->ui }); 2678 | } else 2679 | view(arg); 2680 | } 2681 | 2682 | void 2683 | tagmon(const Arg *arg) 2684 | { 2685 | if (!selmon->sel || !mons->next) 2686 | return; 2687 | sendmon(selmon->sel, dirtomon(arg->i)); 2688 | focusmon(&(Arg) { .i = arg->i }); 2689 | if (selmon->sel && selmon->sel->isfloating) { 2690 | resize(selmon->sel, selmon->mx + (selmon->mw - selmon->sel->w) / 2, selmon->my + (selmon->mh - selmon->sel->h) / 2, selmon->sel->w, selmon->sel->h, 0); 2691 | } 2692 | pointerclient(selmon->sel); 2693 | } 2694 | 2695 | void 2696 | togglesystray() 2697 | { 2698 | if (showsystray) { 2699 | showsystray = 0; 2700 | XUnmapWindow(dpy, systray->win); 2701 | } else { 2702 | showsystray = 1; 2703 | } 2704 | updatesystray(); 2705 | updatestatus(); 2706 | } 2707 | 2708 | void 2709 | togglebar(const Arg *arg) 2710 | { 2711 | selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar; 2712 | updatebarpos(selmon); 2713 | resizebarwin(selmon); 2714 | if (showsystray) { 2715 | XWindowChanges wc; 2716 | if (!selmon->showbar) 2717 | wc.y = -bh; 2718 | else if (selmon->showbar) { 2719 | wc.y = 0; 2720 | if (!selmon->topbar) 2721 | wc.y = selmon->mh - bh; 2722 | } 2723 | XConfigureWindow(dpy, systray->win, CWY, &wc); 2724 | } 2725 | arrange(selmon); 2726 | updatesystray(); 2727 | } 2728 | 2729 | void 2730 | togglefloating(const Arg *arg) 2731 | { 2732 | if (!selmon->sel) 2733 | return; 2734 | if (selmon->sel->isfullscreen) { 2735 | fullscreen(NULL); 2736 | if (selmon->sel->isfloating) 2737 | return; 2738 | } 2739 | 2740 | selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; 2741 | 2742 | if (selmon->sel->isfloating) { 2743 | selmon->sel->x = selmon->wx + selmon->ww / 6, 2744 | selmon->sel->y = selmon->wy + selmon->wh / 6, 2745 | managefloating(selmon->sel); 2746 | resize(selmon->sel, selmon->sel->x, selmon->sel->y, selmon->ww / 3 * 2, selmon->wh / 3 * 2, 0); 2747 | } 2748 | 2749 | arrange(selmon); 2750 | pointerclient(selmon->sel); 2751 | } 2752 | 2753 | void 2754 | toggleallfloating(const Arg *arg) 2755 | { 2756 | Client *c = NULL; 2757 | int somefloating = 0; 2758 | 2759 | if (!selmon->sel || selmon->sel->isfullscreen) 2760 | return; 2761 | 2762 | for (c = selmon->clients; c; c = c->next) 2763 | if (ISVISIBLE(c) && !HIDDEN(c) && c->isfloating) { 2764 | somefloating = 1; 2765 | break; 2766 | } 2767 | 2768 | if (somefloating) { 2769 | for (c = selmon->clients; c; c = c->next) 2770 | if (ISVISIBLE(c) && !HIDDEN(c)) 2771 | c->isfloating = 0; 2772 | arrange(selmon); 2773 | } else { 2774 | for (c = selmon->clients; c; c = c->next) 2775 | if (ISVISIBLE(c) && !HIDDEN(c)) { 2776 | c->isfloating = 1; 2777 | resize(c, c->x + 2 * snap, c->y + 2 * snap, MAX(c->w - 4 * snap, snap) , MAX(c->h - 4 * snap, snap), 0); 2778 | } 2779 | } 2780 | pointerclient(selmon->sel); 2781 | } 2782 | 2783 | void 2784 | togglescratch(const Arg *arg) 2785 | { 2786 | Client *c; 2787 | Monitor *m; 2788 | unsigned int found = 0; 2789 | 2790 | for (m = mons; m && !found; m = m->next) 2791 | for (c = m->clients; c && !(found = c->isscratchpad); c = c->next); 2792 | if (found) { 2793 | if (c->mon == selmon) // 在同屏幕则toggle win状态 2794 | togglewin(&(Arg){.v = c}); 2795 | else { // 不在同屏幕则将win移到当前屏幕 并显示 2796 | sendmon(c, selmon); 2797 | show(c); 2798 | focus(c); 2799 | if (c->isfloating) { 2800 | resize(c, selmon->mx + (selmon->mw - selmon->sel->w) / 2, selmon->my + (selmon->mh - selmon->sel->h) / 2, selmon->sel->w, selmon->sel->h, 0); 2801 | } 2802 | pointerclient(c); 2803 | } 2804 | } else 2805 | spawn(arg); 2806 | } 2807 | 2808 | void 2809 | restorewin(const Arg *arg) { 2810 | int i = hiddenWinStackTop; 2811 | while (i > -1) { 2812 | if (HIDDEN(hiddenWinStack[i]) && ISVISIBLE(hiddenWinStack[i])) { 2813 | show(hiddenWinStack[i]); 2814 | focus(hiddenWinStack[i]); 2815 | restack(selmon); 2816 | // need set jsel) 2829 | return; 2830 | Client *c = (Client *)selmon->sel; 2831 | hide(c); 2832 | } 2833 | 2834 | int 2835 | issinglewin(const Arg *arg) { 2836 | Client *c = NULL; 2837 | int cot = 0; 2838 | 2839 | for (c = selmon->clients; c; c = c->next) { 2840 | if (ISVISIBLE(c) && !HIDDEN(c)) 2841 | cot++; 2842 | if (cot > 1) 2843 | return 0; 2844 | } 2845 | return 1; 2846 | } 2847 | 2848 | void 2849 | togglewin(const Arg *arg) 2850 | { 2851 | Client *c = (Client*)arg->v; 2852 | if (c == selmon->sel) 2853 | hide(c); 2854 | else { 2855 | if (HIDDEN(c)) 2856 | show(c); 2857 | focus(c); 2858 | restack(selmon); 2859 | } 2860 | } 2861 | 2862 | void 2863 | toggleview(const Arg *arg) 2864 | { 2865 | unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); 2866 | 2867 | if (newtagset) { 2868 | selmon->tagset[selmon->seltags] = newtagset; 2869 | focus(NULL); 2870 | arrange(selmon); 2871 | } 2872 | } 2873 | 2874 | void 2875 | toggleglobal(const Arg *arg) 2876 | { 2877 | if (!selmon->sel) 2878 | return; 2879 | if (selmon->sel->isscratchpad) // is scratchpad always global 2880 | return; 2881 | selmon->sel->isglobal ^= 1; 2882 | selmon->sel->tags = selmon->sel->isglobal ? TAGMASK : selmon->tagset[selmon->seltags]; 2883 | focus(NULL); 2884 | } 2885 | 2886 | void 2887 | toggleborder(const Arg *arg) 2888 | { 2889 | int client_count = 0; 2890 | Client *c = NULL; 2891 | // 判断当前是否选中客户端 2892 | if (!selmon->sel) 2893 | return; 2894 | // 判断是否只有一个窗口 2895 | for (c = selmon->clients; c; c = c->next) { 2896 | if (ISVISIBLE(c) && !HIDDEN(c)) { 2897 | client_count ++; 2898 | } 2899 | } 2900 | if (client_count == 1) { 2901 | return; 2902 | } 2903 | selmon->sel->isnoborder ^= 1; 2904 | selmon->sel->bw = selmon->sel->isnoborder ? 0 : borderpx; 2905 | int diff = (selmon->sel->isnoborder ? -1 : 1) * borderpx; 2906 | // TODO: 当有动画效果时 会有闪烁问题 2907 | resizeclient( 2908 | selmon->sel, 2909 | selmon->sel->x - diff, 2910 | selmon->sel->y - diff, 2911 | selmon->sel->w, 2912 | selmon->sel->h 2913 | ); 2914 | focus(NULL); 2915 | } 2916 | 2917 | void 2918 | unfocus(Client *c, int setfocus) 2919 | { 2920 | if (!c) 2921 | return; 2922 | grabbuttons(c, 0); 2923 | XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); 2924 | if (setfocus) { 2925 | XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); 2926 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); 2927 | } 2928 | } 2929 | 2930 | void 2931 | unmanage(Client *c, int destroyed) 2932 | { 2933 | Monitor *m = c->mon; 2934 | XWindowChanges wc; 2935 | 2936 | detach(c); 2937 | detachstack(c); 2938 | if (!destroyed) { 2939 | wc.border_width = c->oldbw; 2940 | XGrabServer(dpy); /* avoid race conditions */ 2941 | XSetErrorHandler(xerrordummy); 2942 | XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ 2943 | XUngrabButton(dpy, AnyButton, AnyModifier, c->win); 2944 | setclientstate(c, WithdrawnState); 2945 | XSync(dpy, False); 2946 | XSetErrorHandler(xerror); 2947 | XUngrabServer(dpy); 2948 | } 2949 | free(c); 2950 | focus(NULL); 2951 | updateclientlist(); 2952 | arrange(m); 2953 | } 2954 | 2955 | void 2956 | unmapnotify(XEvent *e) 2957 | { 2958 | Client *c; 2959 | XUnmapEvent *ev = &e->xunmap; 2960 | 2961 | if ((c = wintoclient(ev->window))) { 2962 | if (ev->send_event) 2963 | setclientstate(c, WithdrawnState); 2964 | else 2965 | unmanage(c, 0); 2966 | } 2967 | else if ((c = wintosystrayicon(ev->window))) { 2968 | /* KLUDGE! sometimes icons occasionally unmap their windows, but do 2969 | * _not_ destroy them. We map those windows back */ 2970 | XMapRaised(dpy, c->win); 2971 | updatesystray(); 2972 | } 2973 | } 2974 | 2975 | void 2976 | updatebars(void) 2977 | { 2978 | unsigned int w; 2979 | Monitor *m; 2980 | XSetWindowAttributes wa = { 2981 | .override_redirect = True, 2982 | .background_pixel = 0, 2983 | .border_pixel = 0, 2984 | .colormap = cmap, 2985 | .event_mask = ButtonPressMask|ExposureMask 2986 | }; 2987 | XClassHint ch = {"dwm", "dwm"}; 2988 | for (m = mons; m; m = m->next) { 2989 | if (m->barwin) 2990 | continue; 2991 | w = m->ww; 2992 | if (showsystray && m == systraytomon(m)) 2993 | w -= getsystraywidth(); 2994 | m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, depth, 2995 | InputOutput, visual, 2996 | CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); 2997 | XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); 2998 | if (showsystray && m == systraytomon(m)) 2999 | XMapRaised(dpy, systray->win); 3000 | XMapRaised(dpy, m->barwin); 3001 | XSetClassHint(dpy, m->barwin, &ch); 3002 | } 3003 | } 3004 | 3005 | void 3006 | updatebarpos(Monitor *m) 3007 | { 3008 | m->wy = m->my; 3009 | m->wh = m->mh; 3010 | if (m->showbar) { 3011 | m->wh = m->wh - vertpad - bh; 3012 | m->by = m->topbar ? m->wy : m->wy + m->wh + vertpad; 3013 | m->wy = m->topbar ? m->wy + bh + vp : m->wy; 3014 | } else 3015 | m->by = -bh - vp; 3016 | } 3017 | 3018 | void 3019 | updateclientlist() 3020 | { 3021 | Client *c; 3022 | Monitor *m; 3023 | 3024 | XDeleteProperty(dpy, root, netatom[NetClientList]); 3025 | for (m = mons; m; m = m->next) 3026 | for (c = m->clients; c; c = c->next) 3027 | XChangeProperty(dpy, root, netatom[NetClientList], 3028 | XA_WINDOW, 32, PropModeAppend, 3029 | (unsigned char *) &(c->win), 1); 3030 | } 3031 | 3032 | int 3033 | updategeom(void) 3034 | { 3035 | int dirty = 0; 3036 | 3037 | #ifdef XINERAMA 3038 | if (XineramaIsActive(dpy)) { 3039 | int i, j, n, nn; 3040 | Client *c; 3041 | Monitor *m; 3042 | XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn); 3043 | XineramaScreenInfo *unique = NULL; 3044 | 3045 | for (n = 0, m = mons; m; m = m->next, n++); 3046 | /* only consider unique geometries as separate screens */ 3047 | unique = ecalloc(nn, sizeof(XineramaScreenInfo)); 3048 | for (i = 0, j = 0; i < nn; i++) 3049 | if (isuniquegeom(unique, j, &info[i])) 3050 | memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); 3051 | XFree(info); 3052 | nn = j; 3053 | if (n <= nn) { /* new monitors available */ 3054 | for (i = 0; i < (nn - n); i++) { 3055 | for (m = mons; m && m->next; m = m->next); 3056 | if (m) 3057 | m->next = createmon(); 3058 | else 3059 | mons = createmon(); 3060 | } 3061 | for (i = 0, m = mons; i < nn && m; m = m->next, i++) 3062 | if (i >= n 3063 | || unique[i].x_org != m->mx || unique[i].y_org != m->my 3064 | || unique[i].width != m->mw || unique[i].height != m->mh) 3065 | { 3066 | dirty = 1; 3067 | m->num = i; 3068 | m->mx = m->wx = unique[i].x_org; 3069 | m->my = m->wy = unique[i].y_org; 3070 | m->mw = m->ww = unique[i].width; 3071 | m->mh = m->wh = unique[i].height; 3072 | updatebarpos(m); 3073 | } 3074 | } else { /* less monitors available nn < n */ 3075 | for (i = nn; i < n; i++) { 3076 | for (m = mons; m && m->next; m = m->next); 3077 | while ((c = m->clients)) { 3078 | dirty = 1; 3079 | m->clients = c->next; 3080 | detachstack(c); 3081 | c->mon = mons; 3082 | attach(c); 3083 | attachstack(c); 3084 | } 3085 | if (m == selmon) 3086 | selmon = mons; 3087 | cleanupmon(m); 3088 | } 3089 | } 3090 | free(unique); 3091 | } else 3092 | #endif /* XINERAMA */ 3093 | { /* default monitor setup */ 3094 | if (!mons) 3095 | mons = createmon(); 3096 | if (mons->mw != sw || mons->mh != sh) { 3097 | dirty = 1; 3098 | mons->mw = mons->ww = sw; 3099 | mons->mh = mons->wh = sh; 3100 | updatebarpos(mons); 3101 | } 3102 | } 3103 | if (dirty) { 3104 | selmon = mons; 3105 | selmon = wintomon(root); 3106 | } 3107 | return dirty; 3108 | } 3109 | 3110 | void 3111 | updatenumlockmask(void) 3112 | { 3113 | unsigned int i, j; 3114 | XModifierKeymap *modmap; 3115 | 3116 | numlockmask = 0; 3117 | modmap = XGetModifierMapping(dpy); 3118 | for (i = 0; i < 8; i++) 3119 | for (j = 0; j < modmap->max_keypermod; j++) 3120 | if (modmap->modifiermap[i * modmap->max_keypermod + j] 3121 | == XKeysymToKeycode(dpy, XK_Num_Lock)) 3122 | numlockmask = (1 << i); 3123 | XFreeModifiermap(modmap); 3124 | } 3125 | 3126 | void 3127 | updatesizehints(Client *c) 3128 | { 3129 | long msize; 3130 | XSizeHints size; 3131 | 3132 | if (!XGetWMNormalHints(dpy, c->win, &size, &msize)) 3133 | /* size is uninitialized, ensure that size.flags aren't used */ 3134 | size.flags = PSize; 3135 | if (size.flags & PBaseSize) { 3136 | c->basew = size.base_width; 3137 | c->baseh = size.base_height; 3138 | } else if (size.flags & PMinSize) { 3139 | c->basew = size.min_width; 3140 | c->baseh = size.min_height; 3141 | } else 3142 | c->basew = c->baseh = 0; 3143 | if (size.flags & PResizeInc) { 3144 | c->incw = size.width_inc; 3145 | c->inch = size.height_inc; 3146 | } else 3147 | c->incw = c->inch = 0; 3148 | if (size.flags & PMaxSize) { 3149 | c->maxw = size.max_width; 3150 | c->maxh = size.max_height; 3151 | } else 3152 | c->maxw = c->maxh = 0; 3153 | if (size.flags & PMinSize) { 3154 | c->minw = size.min_width; 3155 | c->minh = size.min_height; 3156 | } else if (size.flags & PBaseSize) { 3157 | c->minw = size.base_width; 3158 | c->minh = size.base_height; 3159 | } else 3160 | c->minw = c->minh = 0; 3161 | if (size.flags & PAspect) { 3162 | c->mina = (float)size.min_aspect.y / size.min_aspect.x; 3163 | c->maxa = (float)size.max_aspect.x / size.max_aspect.y; 3164 | } else 3165 | c->maxa = c->mina = 0.0; 3166 | c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); 3167 | } 3168 | 3169 | void 3170 | updatestatus(void) 3171 | { 3172 | Monitor *m; 3173 | if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) 3174 | strcpy(stext, "^c#2D1B46^^b#335566^:) ^d^"); // 默认的状态栏文本 3175 | for (m = mons; m; m = m->next) 3176 | drawbar(m); 3177 | updatesystray(); 3178 | } 3179 | 3180 | void 3181 | updatesystrayicongeom(Client *i, int w, int h) 3182 | { 3183 | if (i) { 3184 | i->h = bh; 3185 | if (w == h) 3186 | i->w = bh; 3187 | else if (h == bh) 3188 | i->w = w; 3189 | else 3190 | i->w = (int) ((float)bh * ((float)w / (float)h)); 3191 | applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False); 3192 | /* force icons into the systray dimenons if they don't want to */ 3193 | if (i->h > bh) { 3194 | if (i->w == i->h) 3195 | i->w = bh; 3196 | else 3197 | i->w = (int) ((float)bh * ((float)i->w / (float)i->h)); 3198 | i->h = bh; 3199 | } 3200 | } 3201 | } 3202 | 3203 | void 3204 | updatesystrayiconstate(Client *i, XPropertyEvent *ev) 3205 | { 3206 | long flags; 3207 | int code = 0; 3208 | 3209 | if (!showsystray || !i || ev->atom != xatom[XembedInfo] || 3210 | !(flags = getatomprop(i, xatom[XembedInfo]))) 3211 | return; 3212 | 3213 | if (flags & XEMBED_MAPPED && !i->tags) { 3214 | i->tags = 1; 3215 | code = XEMBED_WINDOW_ACTIVATE; 3216 | XMapRaised(dpy, i->win); 3217 | setclientstate(i, NormalState); 3218 | } 3219 | else if (!(flags & XEMBED_MAPPED) && i->tags) { 3220 | i->tags = 0; 3221 | code = XEMBED_WINDOW_DEACTIVATE; 3222 | XUnmapWindow(dpy, i->win); 3223 | setclientstate(i, WithdrawnState); 3224 | } 3225 | else 3226 | return; 3227 | sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0, 3228 | systray->win, XEMBED_EMBEDDED_VERSION); 3229 | } 3230 | 3231 | void 3232 | updatesystray(void) 3233 | { 3234 | XSetWindowAttributes wa; 3235 | XWindowChanges wc; 3236 | Client *i; 3237 | Monitor *m = systraytomon(NULL); 3238 | unsigned int x = m->mx + m->mw; 3239 | unsigned int w = 1; 3240 | 3241 | if (!showsystray) 3242 | return; 3243 | if (!systray) { 3244 | /* init systray */ 3245 | if (!(systray = (Systray *)calloc(1, sizeof(Systray)))) 3246 | die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); 3247 | systray->win = XCreateSimpleWindow(dpy, root, x - sp, m->by + vp, w, bh, 0, 0, scheme[SchemeSystray][ColBg].pixel); 3248 | wa.event_mask = ButtonPressMask | ExposureMask; 3249 | wa.override_redirect = True; 3250 | wa.background_pixel = scheme[SchemeSystray][ColBg].pixel; 3251 | XSelectInput(dpy, systray->win, SubstructureNotifyMask); 3252 | XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32, 3253 | PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1); 3254 | XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa); 3255 | XMapRaised(dpy, systray->win); 3256 | XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime); 3257 | if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) { 3258 | sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0); 3259 | XSync(dpy, False); 3260 | } 3261 | else { 3262 | fprintf(stderr, "dwm: unable to obtain system tray.\n"); 3263 | free(systray); 3264 | systray = NULL; 3265 | return; 3266 | } 3267 | } 3268 | for (w = 0, i = systray->icons; i; i = i->next) { 3269 | /* make sure the background color stays the same */ 3270 | wa.background_pixel = scheme[SchemeSystray][ColBg].pixel; 3271 | XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa); 3272 | XMapRaised(dpy, i->win); 3273 | w += systrayspacing; 3274 | i->x = w; 3275 | XMoveResizeWindow(dpy, i->win, i->x + 3, 0 + 3, MAX(i->w - 6, bh - 6), bh - 6); // 限制过大的图标 3276 | w += MAX(i->w, bh); 3277 | if (i->mon != m) 3278 | i->mon = m; 3279 | } 3280 | w = w ? w + systrayspacing : 1; 3281 | x -= w; 3282 | XMoveResizeWindow(dpy, systray->win, x - sp, m->by + vp, w, bh); 3283 | wc.x = x - sp; wc.y = m->by + vp; wc.width = w; wc.height = bh; 3284 | wc.stack_mode = Above; wc.sibling = m->barwin; 3285 | XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc); 3286 | XMapWindow(dpy, systray->win); 3287 | XMapSubwindows(dpy, systray->win); 3288 | XSync(dpy, False); 3289 | } 3290 | 3291 | void 3292 | updatetitle(Client *c) 3293 | { 3294 | if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) 3295 | gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); 3296 | if (c->name[0] == '\0') /* hack to mark broken clients */ 3297 | strcpy(c->name, broken); 3298 | } 3299 | 3300 | void 3301 | updatewindowtype(Client *c) 3302 | { 3303 | Atom state = getatomprop(c, netatom[NetWMState]); 3304 | Atom wtype = getatomprop(c, netatom[NetWMWindowType]); 3305 | 3306 | if (state == netatom[NetWMFullscreen]) 3307 | setfullscreen(c, 1); 3308 | if (wtype == netatom[NetWMWindowTypeDialog]) 3309 | c->isfloating = 1; 3310 | } 3311 | 3312 | void 3313 | updatewmhints(Client *c) 3314 | { 3315 | XWMHints *wmh; 3316 | 3317 | if ((wmh = XGetWMHints(dpy, c->win))) { 3318 | if (c == selmon->sel && wmh->flags & XUrgencyHint) { 3319 | wmh->flags &= ~XUrgencyHint; 3320 | XSetWMHints(dpy, c->win, wmh); 3321 | } else 3322 | c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; 3323 | if (wmh->flags & InputHint) 3324 | c->neverfocus = !wmh->input; 3325 | else 3326 | c->neverfocus = 0; 3327 | XFree(wmh); 3328 | } 3329 | } 3330 | 3331 | void 3332 | setgap(const Arg *arg) 3333 | { 3334 | gappi = arg->i ? MAX(gappi + arg->i, 0) : _gappi; 3335 | gappo = arg->i ? MAX(gappo + arg->i, 0) : _gappo; 3336 | arrange(selmon); 3337 | } 3338 | 3339 | void 3340 | view(const Arg *arg) 3341 | { 3342 | int i; 3343 | unsigned int tmptag; 3344 | Client *c; 3345 | int n = 0; 3346 | 3347 | selmon->seltags ^= 1; /* toggle sel tagset */ 3348 | if (arg->ui & TAGMASK) { 3349 | selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; 3350 | selmon->pertag->prevtag = selmon->pertag->curtag; 3351 | 3352 | if (arg->ui == ~0) 3353 | selmon->pertag->curtag = 0; 3354 | else { 3355 | for (i = 0; !(arg->ui & 1 << i); i++) ; 3356 | selmon->pertag->curtag = i + 1; 3357 | } 3358 | } else { 3359 | tmptag = selmon->pertag->prevtag; 3360 | selmon->pertag->prevtag = selmon->pertag->curtag; 3361 | selmon->pertag->curtag = tmptag; 3362 | } 3363 | 3364 | selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; 3365 | selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; 3366 | selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; 3367 | selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; 3368 | selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; 3369 | 3370 | if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) 3371 | togglebar(NULL); 3372 | 3373 | focus(NULL); 3374 | arrange(selmon); 3375 | 3376 | // 若当前tag无窗口 且附加了v参数 则执行 3377 | if (arg->v) { 3378 | for (c = selmon->clients; c; c = c->next) 3379 | if (c->tags & arg->ui && !HIDDEN(c) && !c->isglobal) 3380 | n++; 3381 | if (n == 0) { 3382 | spawn(&(Arg){ .v = (const char*[]){ "/bin/sh", "-c", arg->v, NULL } }); 3383 | } 3384 | } 3385 | } 3386 | 3387 | void 3388 | viewtoleft(const Arg *arg) { 3389 | unsigned int target = selmon->tagset[selmon->seltags], pre; 3390 | Client *c; 3391 | while (1) { 3392 | pre = target; 3393 | target >>= 1; 3394 | if (target == pre) return; 3395 | 3396 | for (c = selmon->clients; c; c = c->next) { 3397 | if (c->isglobal && c->tags == TAGMASK) continue; 3398 | if (c->tags & target && __builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 3399 | && selmon->tagset[selmon->seltags] > 1) { 3400 | view(&(Arg) { .ui = target }); 3401 | return; 3402 | } 3403 | } 3404 | } 3405 | } 3406 | 3407 | void 3408 | viewtoright(const Arg *arg) { 3409 | unsigned int target = selmon->tagset[selmon->seltags]; 3410 | Client *c; 3411 | while (1) { 3412 | target = target == 0 ? 1 : target << 1; 3413 | if (!(target & TAGMASK)) return; 3414 | 3415 | for (c = selmon->clients; c; c = c->next) { 3416 | if (c->isglobal && c->tags == TAGMASK) continue; 3417 | if (c->tags & target && __builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 3418 | && selmon->tagset[selmon->seltags] & (TAGMASK >> 1)) { 3419 | view(&(Arg) { .ui = target }); 3420 | return; 3421 | } 3422 | } 3423 | } 3424 | } 3425 | 3426 | void 3427 | tile(Monitor *m) 3428 | { 3429 | unsigned int i, n, mw, mh, sh, my, sy; // mw: master的宽度, mh: master的高度, sh: stack的高度, my: master的y坐标, sy: stack的y坐标 3430 | Client *c; 3431 | 3432 | for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); 3433 | if (n == 0) return; 3434 | 3435 | if (n > m->nmaster) 3436 | mw = m->nmaster ? (m->ww + gappi) * m->mfact : 0; 3437 | else 3438 | mw = m->ww - 2 * gappo + gappi; 3439 | 3440 | mh = m->nmaster == 0 ? 0 : (m->wh - 2 * gappo - gappi * (m->nmaster - 1)) / m->nmaster; // 单个master的高度 3441 | sh = n == m->nmaster ? 0 : (m->wh - 2 * gappo - gappi * (n - m->nmaster - 1)) / (n - m->nmaster); // 单个stack的高度 3442 | 3443 | for (i = 0, my = sy = gappo, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) 3444 | if (i < m->nmaster) { 3445 | resize(c, 3446 | m->wx + gappo, 3447 | m->wy + my, 3448 | mw - 2 * c->bw - gappi, 3449 | mh - 2 * c->bw, 3450 | 0); 3451 | my += HEIGHT(c) + gappi; 3452 | } else { 3453 | resize(c, 3454 | m->wx + mw + gappo, 3455 | m->wy + sy, 3456 | m->ww - mw - 2 * c->bw - 2 * gappo, 3457 | sh - 2* c->bw, 3458 | 0); 3459 | sy += HEIGHT(c) + gappi; 3460 | } 3461 | } 3462 | 3463 | void 3464 | magicgrid(Monitor *m) 3465 | { 3466 | grid(m, gappo, gappi); 3467 | } 3468 | 3469 | void 3470 | grid(Monitor *m, uint gappo, uint gappi) 3471 | { 3472 | unsigned int i, n; 3473 | unsigned int cx, cy, cw, ch; 3474 | unsigned int dx; 3475 | unsigned int cols, rows, overcols; 3476 | Client *c; 3477 | 3478 | for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); 3479 | if (n == 0) return; 3480 | if (n == 1) { 3481 | c = nexttiled(m->clients); 3482 | cw = (m->ww - 2 * gappo) * 0.7; 3483 | ch = (m->wh - 2 * gappo) * 0.65; 3484 | resize(c, 3485 | m->mx + (m->mw - cw) / 2 + gappo, 3486 | m->my + (m->mh - ch) / 2 + gappo, 3487 | cw - 2 * c->bw, 3488 | ch - 2 * c->bw, 3489 | 0); 3490 | return; 3491 | } 3492 | if (n == 2) { 3493 | c = nexttiled(m->clients); 3494 | cw = (m->ww - 2 * gappo - gappi) / 2; 3495 | ch = (m->wh - 2 * gappo) * 0.65; 3496 | resize(c, 3497 | m->mx + gappo, 3498 | m->my + (m->mh - ch) / 2 + gappo, 3499 | cw - 2 * c->bw, 3500 | ch - 2 * c->bw, 3501 | 0); 3502 | resize(nexttiled(c->next), 3503 | m->mx + cw + gappo + gappi, 3504 | m->my + (m->mh - ch) / 2 + gappo, 3505 | cw - 2 * c->bw, 3506 | ch - 2 * c->bw, 3507 | 0); 3508 | return; 3509 | } 3510 | 3511 | for (cols = 0; cols <= n / 2; cols++) 3512 | if (cols * cols >= n) 3513 | break; 3514 | rows = (cols && (cols - 1) * cols >= n) ? cols - 1 : cols; 3515 | ch = (m->wh - 2 * gappo - (rows - 1) * gappi) / rows; 3516 | cw = (m->ww - 2 * gappo - (cols - 1) * gappi) / cols; 3517 | 3518 | overcols = n % cols; 3519 | if (overcols) dx = (m->ww - overcols * cw - (overcols - 1) * gappi) / 2 - gappo; 3520 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { 3521 | cx = m->wx + (i % cols) * (cw + gappi); 3522 | cy = m->wy + (i / cols) * (ch + gappi); 3523 | if (overcols && i >= n - overcols) { 3524 | cx += dx; 3525 | } 3526 | resize(c, 3527 | cx + gappo, 3528 | cy + gappo, 3529 | cw - 2 * c->bw, 3530 | ch - 2 * c->bw, 3531 | 0); 3532 | } 3533 | } 3534 | 3535 | Client * 3536 | wintoclient(Window w) 3537 | { 3538 | Client *c; 3539 | Monitor *m; 3540 | 3541 | for (m = mons; m; m = m->next) 3542 | for (c = m->clients; c; c = c->next) 3543 | if (c->win == w) 3544 | return c; 3545 | return NULL; 3546 | } 3547 | 3548 | Client * 3549 | wintosystrayicon(Window w) { 3550 | Client *i = NULL; 3551 | 3552 | if (!showsystray || !w) 3553 | return i; 3554 | for (i = systray->icons; i && i->win != w; i = i->next) ; 3555 | return i; 3556 | } 3557 | 3558 | Monitor * 3559 | wintomon(Window w) 3560 | { 3561 | int x, y; 3562 | Client *c; 3563 | Monitor *m; 3564 | 3565 | if (w == root && getrootptr(&x, &y)) 3566 | return recttomon(x, y, 1, 1); 3567 | for (m = mons; m; m = m->next) 3568 | if (w == m->barwin) 3569 | return m; 3570 | if ((c = wintoclient(w))) 3571 | return c->mon; 3572 | return selmon; 3573 | } 3574 | 3575 | /* There's no way to check accesses to destroyed windows, thus those cases are 3576 | * ignored (especially on UnmapNotify's). Other types of errors call Xlibs 3577 | * default error handler, which may call exit. */ 3578 | int 3579 | xerror(Display *dpy, XErrorEvent *ee) 3580 | { 3581 | if (ee->error_code == BadWindow 3582 | || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) 3583 | || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) 3584 | || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) 3585 | || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) 3586 | || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) 3587 | || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) 3588 | || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) 3589 | || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) 3590 | return 0; 3591 | fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", 3592 | ee->request_code, ee->error_code); 3593 | return xerrorxlib(dpy, ee); /* may call exit */ 3594 | } 3595 | 3596 | int 3597 | xerrordummy(Display *dpy, XErrorEvent *ee) 3598 | { 3599 | return 0; 3600 | } 3601 | 3602 | /* Startup Error handler to check if another window manager 3603 | * is already running. */ 3604 | int 3605 | xerrorstart(Display *dpy, XErrorEvent *ee) 3606 | { 3607 | die("dwm: another window manager is already running"); 3608 | return -1; 3609 | } 3610 | 3611 | Monitor * 3612 | systraytomon(Monitor *m) { 3613 | Monitor *t; 3614 | int i, n; 3615 | if(!systraypinning) { 3616 | if(!m) 3617 | return selmon; 3618 | return m == selmon ? m : NULL; 3619 | } 3620 | for(n = 1, t = mons; t && t->next; n++, t = t->next) ; 3621 | for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ; 3622 | if(n < systraypinning) 3623 | return mons; 3624 | return t; 3625 | } 3626 | 3627 | void 3628 | xinitvisual() 3629 | { 3630 | XVisualInfo *infos; 3631 | XRenderPictFormat *fmt; 3632 | int nitems; 3633 | int i; 3634 | 3635 | XVisualInfo tpl = { 3636 | .screen = screen, 3637 | .depth = 32, 3638 | .class = TrueColor 3639 | }; 3640 | long masks = VisualScreenMask | VisualDepthMask | VisualClassMask; 3641 | 3642 | infos = XGetVisualInfo(dpy, masks, &tpl, &nitems); 3643 | visual = NULL; 3644 | for(i = 0; i < nitems; i ++) { 3645 | fmt = XRenderFindVisualFormat(dpy, infos[i].visual); 3646 | if (fmt->type == PictTypeDirect && fmt->direct.alphaMask) { 3647 | visual = infos[i].visual; 3648 | depth = infos[i].depth; 3649 | cmap = XCreateColormap(dpy, root, visual, AllocNone); 3650 | useargb = 1; 3651 | break; 3652 | } 3653 | } 3654 | 3655 | XFree(infos); 3656 | 3657 | if (! visual) { 3658 | visual = DefaultVisual(dpy, screen); 3659 | depth = DefaultDepth(dpy, screen); 3660 | cmap = DefaultColormap(dpy, screen); 3661 | } 3662 | } 3663 | 3664 | void 3665 | zoom(const Arg *arg) 3666 | { 3667 | Client *c = selmon->sel; 3668 | 3669 | if (c && (c->isfloating || c->isfullscreen)) 3670 | return; 3671 | if (c == nexttiled(selmon->clients)) 3672 | if (!c || !(c = nexttiled(c->next))) 3673 | return; 3674 | pop(c); 3675 | } 3676 | 3677 | void 3678 | previewallwin() { 3679 | Monitor *m = selmon; 3680 | Client *c, *focus_c = NULL; 3681 | 3682 | // 排布所有窗口的预览座标 3683 | unsigned int n; 3684 | for (n = 0, c = m->clients; c; c = c->next, n++); 3685 | if (n == 0) return; 3686 | setpreviewwins(n, m, 60, 15); 3687 | 3688 | XEvent event; 3689 | while (1) { 3690 | XNextEvent(dpy, &event); 3691 | if (event.type == KeyPress) { 3692 | if (CLEANMASK(event.xkey.state) != MODKEY) continue; 3693 | 3694 | KeySym keysym = XKeycodeToKeysym(dpy, event.xkey.keycode, 0); 3695 | if (keysym == XK_a) { 3696 | focuspreviewwin(focus_c, m); 3697 | break; 3698 | } 3699 | if (keysym == XK_Tab) { 3700 | // 移除当前预览窗口的边框 3701 | if (focus_c) XSetWindowBorder(dpy, focus_c->preview.win, scheme[SchemeNorm][ColBorder].pixel); 3702 | if (!focus_c) focus_c = m->clients; 3703 | else focus_c = focus_c->next ? focus_c->next : m->clients; 3704 | if (focus_c) { 3705 | XSetWindowBorder(dpy, focus_c->preview.win, scheme[SchemeSel][ColBorder].pixel); 3706 | XWarpPointer(dpy, None, root, 0, 0, 0, 0, focus_c->preview.x + focus_c->preview.scaled_image->width / 2, focus_c->preview.y + focus_c->preview.scaled_image->height / 2); 3707 | } 3708 | } 3709 | } 3710 | if (event.type == ButtonPress && event.xbutton.button == Button1) { 3711 | focuspreviewwin(focus_c, m); 3712 | break; 3713 | } 3714 | if (event.type == EnterNotify) { 3715 | for (c = m->clients; c; c = c->next) 3716 | if (event.xcrossing.window == c->preview.win) { 3717 | focus_c = c; 3718 | XSetWindowBorder(dpy, c->preview.win, scheme[SchemeSel][ColBorder].pixel); 3719 | break; 3720 | } 3721 | } 3722 | if (event.type == LeaveNotify) { 3723 | for (c = m->clients; c; c = c->next) 3724 | if (event.xcrossing.window == c->preview.win) { 3725 | XSetWindowBorder(dpy, c->preview.win, scheme[SchemeNorm][ColBorder].pixel); 3726 | break; 3727 | } 3728 | } 3729 | } 3730 | 3731 | arrange(m); 3732 | pointerclient(focus_c); 3733 | focus(focus_c); 3734 | } 3735 | 3736 | void 3737 | focuspreviewwin(Client *focus_c, Monitor *m) { 3738 | Client *c; 3739 | for (c = m->clients; c; c = c->next) { 3740 | if (c->preview.win) { 3741 | XUnmapWindow(dpy, c->preview.win); 3742 | XMapWindow(dpy, c->win); 3743 | } 3744 | if (c->preview.scaled_image) XDestroyImage(c->preview.scaled_image); 3745 | } 3746 | 3747 | if (focus_c) { 3748 | show(focus_c); 3749 | selmon->seltags ^= 1; 3750 | m->tagset[selmon->seltags] = focus_c->tags; 3751 | } 3752 | } 3753 | 3754 | void 3755 | setpreviewwins(unsigned int n, Monitor *m, unsigned int gappo, unsigned int gappi) { 3756 | unsigned int cx, cy, cw, ch, cmaxh; 3757 | unsigned int cols, rows; 3758 | Client *c = m->clients, *tmpc; 3759 | 3760 | for (cols = 0; cols <= n / 2; cols++) if (cols * cols >= n) break; 3761 | rows = (cols && (cols - 1) * cols >= n) ? cols - 1 : cols; 3762 | ch = (m->wh - 2 * gappo) / rows; 3763 | cw = (m->ww - 2 * gappo) / cols; 3764 | 3765 | cx = 0; 3766 | cy = 0; 3767 | 3768 | unsigned int i, j; 3769 | c = m->clients; 3770 | 3771 | for (i = 0; i < rows; i++) { 3772 | cx = 0; 3773 | cmaxh = 0; 3774 | tmpc = c; 3775 | for (int j = 0; j < cols; j++) { 3776 | if (!c) break; 3777 | c->preview.scaled_image = scaledownimage(c, cw, ch); 3778 | c->preview.x = cx; 3779 | cmaxh = c->preview.scaled_image->height > cmaxh ? c->preview.scaled_image->height : cmaxh; 3780 | cx += c->preview.scaled_image->width + gappi; 3781 | c = c->next; 3782 | } 3783 | c = tmpc; 3784 | cx = m->wx + (m->ww - cx) / 2; 3785 | for (j = 0; j < cols; j++) { 3786 | if (!c) break; 3787 | c->preview.x += cx; 3788 | c->preview.y = cy + (cmaxh - c->preview.scaled_image->height) / 2; 3789 | c = c->next; 3790 | } 3791 | cy += cmaxh + gappi; 3792 | } 3793 | cy = m->wy + (m->wh - cy) / 2; 3794 | for (c = m->clients; c; c = c->next) 3795 | c->preview.y += cy; 3796 | 3797 | 3798 | for (Client *c = m->clients; c; c = c->next) { 3799 | if (!c->preview.win) c->preview.win = XCreateSimpleWindow(dpy, root, c->preview.x, c->preview.y, c->preview.scaled_image->width, c->preview.scaled_image->height, 1, BlackPixel(dpy, screen), WhitePixel(dpy, screen)); 3800 | else XMoveResizeWindow(dpy, c->preview.win, c->preview.x, c->preview.y, c->preview.scaled_image->width, c->preview.scaled_image->height); 3801 | XSetWindowBorder(dpy, c->preview.win, scheme[SchemeNorm][ColBorder].pixel); 3802 | XUnmapWindow(dpy, c->win); 3803 | if (c->preview.win) { 3804 | XSelectInput(dpy, c->preview.win, ButtonPress | EnterWindowMask | LeaveWindowMask); 3805 | XMapWindow(dpy, c->preview.win); 3806 | GC gc = XCreateGC(dpy, c->preview.win, 0, NULL); 3807 | XPutImage(dpy, c->preview.win, gc, c->preview.scaled_image, 0, 0, 0, 0, c->preview.scaled_image->width, c->preview.scaled_image->height); 3808 | } 3809 | } 3810 | } 3811 | 3812 | XImage 3813 | *getwindowximage(Client *c) { 3814 | XWindowAttributes attr; 3815 | XGetWindowAttributes(dpy, c->win, &attr); 3816 | XRenderPictFormat *format = XRenderFindVisualFormat(dpy, attr.visual); 3817 | int hasAlpha = (format->type == PictTypeDirect && format->direct.alphaMask); 3818 | XRenderPictureAttributes pa; 3819 | pa.subwindow_mode = IncludeInferiors; 3820 | Picture picture = XRenderCreatePicture(dpy, c->win, format, CPSubwindowMode, &pa); 3821 | Pixmap pixmap = XCreatePixmap(dpy, root, c->w, c->h, 32); 3822 | XRenderPictureAttributes pa2; 3823 | XRenderPictFormat *format2 = XRenderFindStandardFormat(dpy, PictStandardARGB32); 3824 | Picture pixmapPicture = XRenderCreatePicture(dpy, pixmap, format2, 0, &pa2); 3825 | XRenderColor color; 3826 | color.red = 0x0000; 3827 | color.green = 0x0000; 3828 | color.blue = 0x0000; 3829 | color.alpha = 0x0000; 3830 | XRenderFillRectangle(dpy, PictOpSrc, pixmapPicture, &color, 0, 0, c->w, c->h); 3831 | XRenderComposite(dpy, hasAlpha ? PictOpOver : PictOpSrc, picture, 0, pixmapPicture, 0, 0, 0, 0, 0, 0, c->w, c->h); 3832 | XImage *img = XGetImage(dpy, pixmap, 0, 0, c->w, c->h, AllPlanes, ZPixmap); 3833 | img->red_mask = format2->direct.redMask << format2->direct.red; 3834 | img->green_mask = format2->direct.greenMask << format2->direct.green; 3835 | img->blue_mask = format2->direct.blueMask << format2->direct.blue; 3836 | img->depth = DefaultDepth(dpy, screen); 3837 | return img; 3838 | } 3839 | 3840 | XImage 3841 | *scaledownimage(Client *c, unsigned int cw, unsigned int ch) { 3842 | XImage *orig_image = getwindowximage(c); 3843 | int factor_w = orig_image->width / cw + 1; 3844 | int factor_h = orig_image->height / ch + 1; 3845 | int scale_factor = factor_w > factor_h ? factor_w : factor_h; 3846 | int scaled_width = orig_image->width / scale_factor; 3847 | int scaled_height = orig_image->height / scale_factor; 3848 | XImage *scaled_image = XCreateImage(dpy, DefaultVisual(dpy, DefaultScreen(dpy)), orig_image->depth, ZPixmap, 0, NULL, scaled_width, scaled_height, 32, 0); 3849 | scaled_image->data = malloc(scaled_image->height * scaled_image->bytes_per_line); 3850 | for (int y = 0; y < scaled_height; y++) { 3851 | for (int x = 0; x < scaled_width; x++) { 3852 | int orig_x = x * scale_factor; 3853 | int orig_y = y * scale_factor; 3854 | unsigned long pixel = XGetPixel(orig_image, orig_x, orig_y); 3855 | XPutPixel(scaled_image, x, y, pixel); 3856 | } 3857 | } 3858 | scaled_image->depth = orig_image->depth; 3859 | return scaled_image; 3860 | } 3861 | 3862 | int 3863 | main(int argc, char *argv[]) 3864 | { 3865 | if (argc == 2 && !strcmp("-v", argv[1])) 3866 | die("dwm-6.3"); 3867 | else if (argc != 1) 3868 | die("usage: dwm [-v]"); 3869 | if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) 3870 | fputs("warning: no locale support\n", stderr); 3871 | if (!(dpy = XOpenDisplay(NULL))) 3872 | die("dwm: cannot open display"); 3873 | checkotherwm(); 3874 | setup(); 3875 | #ifdef __OpenBSD__ 3876 | if (pledge("stdio rpath proc exec", NULL) == -1) 3877 | die("pledge"); 3878 | #endif /* __OpenBSD__ */ 3879 | scan(); 3880 | runAutostart(); 3881 | run(); 3882 | cleanup(); 3883 | XCloseDisplay(dpy); 3884 | return EXIT_SUCCESS; 3885 | } 3886 | 3887 | Client *direction_select(const Arg *arg) { 3888 | Client *tempClients[100]; 3889 | Client *c = NULL, *tc = selmon->sel; 3890 | int last = -1, issingle = issinglewin(NULL); 3891 | 3892 | if (tc && tc->isfullscreen) /* no support for focusstack with fullscreen windows */ 3893 | return NULL; 3894 | if (!tc) 3895 | tc = selmon->clients; 3896 | if (!tc) 3897 | return NULL; 3898 | 3899 | for (c = selmon->clients; c; c = c->next) { 3900 | if (ISVISIBLE(c) && (issingle || !HIDDEN(c))) { 3901 | last ++; 3902 | tempClients[last] = c; 3903 | } 3904 | } 3905 | 3906 | if (last < 0) return NULL; 3907 | int sel_x=tc->x; 3908 | int sel_y=tc->y; 3909 | long long int distance=LLONG_MAX; 3910 | Client *tempFocusClients=NULL; 3911 | 3912 | switch (arg->i) { 3913 | case UP: 3914 | for (int _i = 0; _i <= last; _i++) { 3915 | if (tempClients[_i]->y < sel_y && tempClients[_i]->x == sel_x) { 3916 | int dis_x = tempClients[_i]->x - sel_x; 3917 | int dis_y = tempClients[_i]->y - sel_y; 3918 | long long int tmp_distance = 3919 | dis_x * dis_x + dis_y * dis_y; // 计算距离 3920 | if (tmp_distance < distance) { 3921 | distance = tmp_distance; 3922 | tempFocusClients = tempClients[_i]; 3923 | } 3924 | } 3925 | } 3926 | if (!tempFocusClients) { 3927 | distance = LLONG_MAX; 3928 | for (int _i = 0; _i <= last; _i++) { 3929 | if (tempClients[_i]->y < sel_y) { 3930 | int dis_x = tempClients[_i]->x - sel_x; 3931 | int dis_y = tempClients[_i]->y - sel_y; 3932 | long long int tmp_distance = 3933 | dis_x * dis_x + dis_y * dis_y; // 计算距离 3934 | if (tmp_distance < distance) { 3935 | distance = tmp_distance; 3936 | tempFocusClients = tempClients[_i]; 3937 | } 3938 | } 3939 | } 3940 | } 3941 | if (tempFocusClients && tempFocusClients->x <= 16384 && 3942 | tempFocusClients->y <= 16384) { 3943 | c = tempFocusClients; 3944 | } 3945 | break; 3946 | case DOWN: 3947 | for (int _i = 0; _i <= last; _i++) { 3948 | if (tempClients[_i]->y > sel_y && tempClients[_i]->x == sel_x) { 3949 | int dis_x = tempClients[_i]->x - sel_x; 3950 | int dis_y = tempClients[_i]->y - sel_y; 3951 | long long int tmp_distance = 3952 | dis_x * dis_x + dis_y * dis_y; // 计算距离 3953 | if (tmp_distance < distance) { 3954 | distance = tmp_distance; 3955 | tempFocusClients = tempClients[_i]; 3956 | } 3957 | } 3958 | } 3959 | if (!tempFocusClients) { 3960 | distance = LLONG_MAX; 3961 | for (int _i = 0; _i <= last; _i++) { 3962 | if (tempClients[_i]->y > sel_y) { 3963 | int dis_x = tempClients[_i]->x - sel_x; 3964 | int dis_y = tempClients[_i]->y - sel_y; 3965 | long long int tmp_distance = 3966 | dis_x * dis_x + dis_y * dis_y; // 计算距离 3967 | if (tmp_distance < distance) { 3968 | distance = tmp_distance; 3969 | tempFocusClients = tempClients[_i]; 3970 | } 3971 | } 3972 | } 3973 | } 3974 | if (tempFocusClients && tempFocusClients->x <= 16384 && 3975 | tempFocusClients->y <= 16384) { 3976 | c = tempFocusClients; 3977 | } 3978 | break; 3979 | case LEFT: 3980 | for (int _i = 0; _i <= last; _i++) { 3981 | if (tempClients[_i]->x < sel_x && tempClients[_i]->y == sel_y) { 3982 | int dis_x = tempClients[_i]->x - sel_x; 3983 | int dis_y = tempClients[_i]->y - sel_y; 3984 | long long int tmp_distance = 3985 | dis_x * dis_x + dis_y * dis_y; // 计算距离 3986 | if (tmp_distance < distance) { 3987 | distance = tmp_distance; 3988 | tempFocusClients = tempClients[_i]; 3989 | } 3990 | } 3991 | } 3992 | if (!tempFocusClients) { 3993 | distance = LLONG_MAX; 3994 | for (int _i = 0; _i <= last; _i++) { 3995 | if (tempClients[_i]->x < sel_x) { 3996 | int dis_x = tempClients[_i]->x - sel_x; 3997 | int dis_y = tempClients[_i]->y - sel_y; 3998 | long long int tmp_distance = 3999 | dis_x * dis_x + dis_y * dis_y; // 计算距离 4000 | if (tmp_distance < distance) { 4001 | distance = tmp_distance; 4002 | tempFocusClients = tempClients[_i]; 4003 | } 4004 | } 4005 | } 4006 | } 4007 | if (tempFocusClients && tempFocusClients->x <= 16384 && 4008 | tempFocusClients->y <= 16384) { 4009 | c = tempFocusClients; 4010 | } 4011 | break; 4012 | case RIGHT: 4013 | for (int _i = 0; _i <= last; _i++) { 4014 | // 第一步先筛选出右边的窗口 优先选择同一层次的 4015 | if (tempClients[_i]->x > sel_x && tempClients[_i]->y == sel_y) { 4016 | int dis_x = tempClients[_i]->x - sel_x; 4017 | int dis_y = tempClients[_i]->y - sel_y; 4018 | long long int tmp_distance = 4019 | dis_x * dis_x + dis_y * dis_y; // 计算距离 4020 | if (tmp_distance < distance) { 4021 | distance = tmp_distance; 4022 | tempFocusClients = tempClients[_i]; 4023 | } 4024 | } 4025 | } 4026 | // 没筛选到,再去除同一层次的要求,重新筛选 4027 | if (!tempFocusClients) { 4028 | distance = LLONG_MAX; 4029 | for (int _i = 0; _i <= last; _i++) { 4030 | if (tempClients[_i]->x > sel_x) { 4031 | int dis_x = tempClients[_i]->x - sel_x; 4032 | int dis_y = tempClients[_i]->y - sel_y; 4033 | long long int tmp_distance = 4034 | dis_x * dis_x + dis_y * dis_y; // 计算距离 4035 | if (tmp_distance < distance) { 4036 | distance = tmp_distance; 4037 | tempFocusClients = tempClients[_i]; 4038 | } 4039 | } 4040 | } 4041 | } 4042 | // 确认选择 4043 | if (tempFocusClients && tempFocusClients->x <= 16384 && 4044 | tempFocusClients->y <= 16384) { 4045 | c = tempFocusClients; 4046 | } 4047 | } 4048 | return c; 4049 | } 4050 | 4051 | void focusdir(const Arg *arg) { 4052 | Client *c = NULL; 4053 | int issingle = issinglewin(NULL); 4054 | 4055 | c = direction_select(arg); 4056 | 4057 | if (issingle) { 4058 | if (c) 4059 | hideotherwins(&(Arg){.v = c}); 4060 | } else { 4061 | if (c) { 4062 | pointerclient(c); 4063 | restack(selmon); 4064 | } 4065 | } 4066 | } 4067 | 4068 | void exchange_two_client(Client *c1, Client *c2) { 4069 | if (c1 == NULL || c2 == NULL || c1->mon != c2->mon) { 4070 | return; 4071 | } 4072 | 4073 | // 先找c1的上一个节点 4074 | Client head1; 4075 | Client *headp1 = &head1; 4076 | headp1->next = selmon->clients; 4077 | Client *tmp1 = headp1; 4078 | for (; tmp1 != NULL; tmp1 = tmp1->next) { 4079 | if (tmp1->next != NULL) { 4080 | if (tmp1->next == c1) 4081 | break; 4082 | } else { 4083 | break; 4084 | } 4085 | } 4086 | 4087 | // 再找c2的上一个节点 4088 | Client head2; 4089 | Client *headp2 = &head2; 4090 | headp2->next = selmon->clients; 4091 | Client *tmp2 = headp2; 4092 | for (; tmp2 != NULL; tmp2 = tmp2->next) { 4093 | if (tmp2->next != NULL) { 4094 | if (tmp2->next == c2) 4095 | break; 4096 | } else { 4097 | break; 4098 | } 4099 | } 4100 | 4101 | if (tmp1 == NULL) { /* gDebug("tmp1==null"); */ 4102 | return; 4103 | } 4104 | if (tmp2 == NULL) { /* gDebug("tmp2==null"); */ 4105 | return; 4106 | } 4107 | if (tmp1->next == NULL) { /* gDebug("tmp1->next==null"); */ 4108 | return; 4109 | } 4110 | if (tmp2->next == NULL) { /* gDebug("tmp2->next==null"); */ 4111 | return; 4112 | } 4113 | 4114 | // 当c1和c2为相邻节点时 4115 | if (c1->next == c2) { 4116 | c1->next = c2->next; 4117 | c2->next = c1; 4118 | tmp1->next = c2; 4119 | } else if (c2->next == c1) { 4120 | c2->next = c1->next; 4121 | c1->next = c2; 4122 | tmp2->next = c1; 4123 | } else { // 不为相邻节点 4124 | tmp1->next = c2; 4125 | tmp2->next = c1; 4126 | Client *tmp = c1->next; 4127 | c1->next = c2->next; 4128 | c2->next = tmp; 4129 | } 4130 | 4131 | // 当更换节点为头节点时,重置头节点 4132 | if (c1 == selmon->clients) { 4133 | selmon->clients = c2; 4134 | } else if (c2 == selmon->clients) { 4135 | selmon->clients = c1; 4136 | } 4137 | 4138 | focus(c1); 4139 | arrange(c1->mon); 4140 | pointerclient(c1); 4141 | } 4142 | 4143 | void exchange_client(const Arg *arg) { 4144 | Client *c = selmon->sel; 4145 | if (!c || c->isfloating || c->isfullscreen) 4146 | return; 4147 | exchange_two_client(c, direction_select(arg)); 4148 | } 4149 | --------------------------------------------------------------------------------