├── LICENSE ├── README.md ├── closest.sh ├── deletelock.sh ├── focus.sh ├── focus_watcher.sh ├── fullscreen.sh ├── groups.sh ├── killwa ├── Makefile ├── arg.h ├── config.mk ├── killwa.c ├── util.c └── util.h ├── rainbow.sh ├── snap.sh ├── switch_grid.sh ├── tile.sh ├── underneath.sh ├── wmenu.sh └── workspace.sh /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2015, brosephs , 2 | 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 10 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 13 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | PERFORMANCE OF THIS SOFTWARE. 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | contrib 2 | ======= 3 | 4 | Repository hosting inspirational wmutils snippets. 5 | 6 | ### tile.sh 7 | Arrange your windows in a tiled pattern consisting of one master area, and a 8 | stacking area. Uses `$GAP` and `$MASTER` from environment. 9 | 10 | usage: tile.sh 11 | 12 | ### fullscreen.sh 13 | Set a window in fullscreen mode, removing borders from it and putting it in 14 | front of all other windows. There can only be one window in fullscreen at a 15 | time. Uses `$FSFILE` from environment. 16 | 17 | usage: fullscreen.sh 18 | 19 | ### focus.sh 20 | Focus either a specific window, or the next/previous window in the stack. Then 21 | set borders accordingly. Uses `$BW`, `$ACTIVE` and `$INACTIVE` from environment. 22 | 23 | usage: focus.sh 24 | 25 | ### focus\_watcher.sh 26 | Focus a new window (using focus.sh) upon its creation. 27 | 28 | Depends on `wew(1)` (opt repo) and `focus.sh` (contrib repo) 29 | 30 | usage: focus_watcher.sh 31 | 32 | ### rainbow.sh 33 | Make the current window border cycle all the rainbow colors. Uses `$FREQ` from 34 | environment. 35 | 36 | usage: rainbow.sh 37 | 38 | ### closest.sh 39 | Focus the closest window in a specific direction. 40 | 41 | usage: closest.sh 42 | 43 | ### underneath.sh 44 | Produce window id directely underneath cursor. 45 | 46 | Example sxhkd binding: 47 | 48 | ~button1 49 | focus.sh $(underneath.sh) 50 | 51 | ### switch\_grid.sh 52 | A simpler version of OS X Mission Control feature, or GNOME Shell's Overview 53 | feature. 54 | 55 | Depends on `wew(1)` (opt repo) and `focus.sh` (contrib repo) 56 | 57 | usage: switch_grid.sh 58 | 59 | ### deletelock.sh 60 | Set a custom xprop variable which can be used to test if a window is able to 61 | be deleted or not when using killw in a custom script with `wew(1)`. 62 | 63 | Depends on xorg-xprop 64 | 65 | usage: deletelock.sh 66 | 67 | ### groups.sh 68 | Adds group-like capabilities, sorta like those you find in CWM and such WMs. 69 | 70 | usage: groups.sh [-hCU] [-c wid] [-s wid group] [-tmMu group] 71 | -h shows this help 72 | -c cleans WID from group files (and makes it visible) 73 | -C runs cleanup routine 74 | -s sets WID's group 75 | -t toggle group visibility state 76 | -m maps (shows) group 77 | -M maps group and unmaps all other groups 78 | -u unmaps (hides) group 79 | -U unmaps all the groups 80 | 81 | ### wmenu.sh 82 | 83 | Uses wname and dmenu to produce a window selection menu similar to CWM's 84 | menu-window option. Upon selection the script outputs the chosen window id that 85 | can then be piped to a focus script. 86 | 87 | ### workspace.sh 88 | Groups windows into workspaces. Stores the current workspace index in /tmp/workspaces/curr by default. 89 | 90 | usage: workspace.sh [-hinp] [-g ws_num] [-m ws_num] 91 | -h: Displays this message 92 | -i: Initialize workspaces. Should be called once in a startup script. 93 | -n: Move up one workspace 94 | -p: Move down one workspace 95 | -g, : go to the workspace specified by 96 | -m, : move the currently focused window to the worskpace specified by 97 | 98 | 99 | *... to be continued* 100 | -------------------------------------------------------------------------------- /closest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # z3bra - 2014 (c) wtfpl 4 | # find and focus the closest window in a specific direction 5 | # depends on: focus.sh 6 | 7 | # get current window id 8 | CUR=$(pfw) 9 | 10 | usage() { 11 | echo "usage: $(basename $0) " >&2 12 | exit 1 13 | } 14 | 15 | next_west() { 16 | lsw | xargs wattr xi | sort -nr | sed "0,/$CUR/d" | sed "1s/^[0-9]* //p;d" 17 | } 18 | 19 | next_east() { 20 | lsw | xargs wattr xi | sort -n | sed "0,/$CUR/d" | sed "1s/^[0-9]* //p;d" 21 | } 22 | 23 | next_north() { 24 | lsw | xargs wattr yi | sort -nr | sed "0,/$CUR/d" | sed "1s/^[0-9]* //p;d" 25 | } 26 | 27 | next_south() { 28 | lsw | xargs wattr yi | sort -n | sed "0,/$CUR/d" | sed "1s/^[0-9]* //p;d" 29 | } 30 | 31 | # Use the specification of your choice: WASD, HJKL, ←↑↓→, west/north/south/east 32 | case $1 in 33 | h|a|west|left) wid=$(next_west) ;; 34 | j|s|south|down) wid=$(next_south) ;; 35 | k|w|north|up) wid=$(next_north) ;; 36 | l|d|east|right) wid=$(next_east) ;; 37 | *) usage ;; 38 | esac 39 | 40 | test ! -z "$wid" && focus.sh "$wid" 41 | -------------------------------------------------------------------------------- /deletelock.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # wildefyr - 2015 (c) wtfpl 4 | # toggle delete lock for current window 5 | 6 | usage() { 7 | echo "usage: $(basename $0) " 8 | exit 1 9 | } 10 | 11 | wid=$(pfw) 12 | 13 | case $2 in 14 | 0x*) 15 | wid=$2 16 | ;; 17 | *) 18 | usage 19 | ;; 20 | esac 21 | 22 | case $1 in 23 | lock) 24 | xprop -id $wid -f _WMUTILS_DELETELOCK 8i -set _WMUTILS_DELETELOCK '1' 25 | ;; 26 | unlock) 27 | xprop -id $wid -remove _WMUTILS_DELETELOCK 28 | ;; 29 | toggle) 30 | lockStatus=$(xprop -id $wid _WMUTILS_DELETELOCK | cut -d\ -f 3) 31 | case $lockStatus in 32 | 1) 33 | $(basename $0) unlock $wid 34 | ;; 35 | *) 36 | $(basename $0) lock $wid 37 | ;; 38 | esac 39 | ;; 40 | status) 41 | lockStatus=$(xprop -id $wid _WMUTILS_DELETELOCK | cut -d\ -f 3) 42 | case $lockStatus in 43 | 1) 44 | echo "1" 45 | ;; 46 | *) 47 | echo "0" 48 | ;; 49 | esac 50 | ;; 51 | *) 52 | usage 53 | ;; 54 | esac 55 | -------------------------------------------------------------------------------- /focus.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # z3bra - 2014 (c) wtfpl 4 | # window focus wrapper that sets borders and can focus next/previous window 5 | 6 | BW=${BW:-2} # border width 7 | ACTIVE=${ACTIVE:-0xffffff} # active border color 8 | INACTIVE=${INACTIVE:-0x333333} # inactive border color 9 | 10 | # get current window id 11 | CUR=$(pfw) 12 | 13 | usage() { 14 | echo "usage: $(basename $0) " 15 | exit 1 16 | } 17 | 18 | setborder() { 19 | ROOT=$(lsw -r) 20 | 21 | # check if window exists 22 | wattr $2 || return 23 | 24 | # do not modify border of fullscreen windows 25 | test "$(wattr xywh $2)" = "$(wattr xywh $ROOT)" && return 26 | 27 | case $1 in 28 | active) chwb -s $BW -c $ACTIVE $2 ;; 29 | inactive) chwb -s $BW -c $INACTIVE $2 ;; 30 | esac 31 | } 32 | 33 | case $1 in 34 | next) wid=$(lsw|grep -v $CUR|sed '1 p;d') ;; 35 | prev) wid=$(lsw|grep -v $CUR|sed '$ p;d') ;; 36 | 0x*) wattr $1 && wid=$1 ;; 37 | *) usage ;; 38 | esac 39 | 40 | # exit if we can't find another window to focus 41 | test -z "$wid" && echo "$(basename $0): can't find a window to focus" >&2 && exit 1 42 | 43 | setborder inactive $CUR # set inactive border on current window 44 | setborder active $wid # activate the new window 45 | chwso -r $wid # put it on top of the stack 46 | wtf $wid # set focus on it 47 | 48 | # you might want to remove this for sloppy focus 49 | wmp -a $(wattr xy $wid) # move the mouse cursor to 50 | wmp -r $(wattr wh $wid) # .. its bottom right corner 51 | -------------------------------------------------------------------------------- /focus_watcher.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # z3bra - 2014 (c) wtfpl 4 | # focus a window when it is created 5 | # depends on: wew focus.sh 6 | 7 | wew | while read ev wid args; do 8 | case $ev in 9 | # occurs on mapping requests 10 | MAP) wattr o $wid || focus.sh $wid ;; 11 | esac 12 | done 13 | -------------------------------------------------------------------------------- /fullscreen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # z3bra - 2014 (c) wtfpl 4 | # toggle the fullscreen state of a window 5 | # depends on: focus.sh 6 | 7 | # this file is used to store the previous geometry of a window 8 | FSFILE=${FSFILE:-~/.fwin} 9 | 10 | # it's pretty simple, but anyway... 11 | usage() { 12 | echo "usage: $(basename $0) " 13 | exit 1 14 | } 15 | 16 | # exit if no argument given 17 | test -z "$1" && usage 18 | 19 | # this will unset the fullscreen state of any fullscreen window if there is one. 20 | # this way, there will only be one window in fullscreen at a time, and no window 21 | # will loose their previous geometry info 22 | test -f $FSFILE && wtp $(cat $FSFILE) 23 | 24 | # if file exist and contain our window id, it means that out window is in 25 | # fullscreen mode 26 | if test -f $FSFILE && grep -q $1 $FSFILE; then 27 | # if the window we removed was our window, delete the file, so we can 28 | # fullscreen it again later 29 | rm -f $FSFILE 30 | 31 | else 32 | # if not, then put the current window in fullscreen mode, after saving its 33 | # geometry and id to $FSFILE we also remove any border from this window. 34 | wattr xywhi $1 > $FSFILE 35 | chwb -s 0 $1 36 | wtp $(wattr xywh `lsw -r`) $1 37 | fi 38 | 39 | # now focus the window, and put it in front, no matter which state we're in, and 40 | # put the cursor on its bottom-right corner (for consistency) 41 | focus.sh $1 42 | -------------------------------------------------------------------------------- /groups.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright (c) 2015 Greduan , licensed under the WTFPL 4 | # Adds group-like capabilities, sorta like those you find in CWM and such WMs 5 | 6 | usage() { 7 | cat << EOF 8 | usage: $(basename $0) [-hCU] [-c wid] [-s wid group] [-tmMu group] 9 | -h shows this help 10 | -c cleans WID from group files (and makes it visible) 11 | -C runs cleanup routine 12 | -s sets WID's group 13 | -t toggle group visibility state 14 | -m maps (shows) group 15 | -M maps group and unmaps all other groups 16 | -u unmaps (hides) group 17 | -U unmaps all the groups 18 | EOF 19 | 20 | exit 1 21 | } 22 | 23 | # test for no arguments 24 | test $# -eq 0 && usage 25 | 26 | # I suggest it's under /tmp or somewhere that gets cleaned up at reboot or gets 27 | # cleaned up after X stops running 28 | FSDIR=${FSDIR:-/tmp/groups.sh} 29 | 30 | # define our functions 31 | 32 | # clean WID ($1) from group files 33 | clean_wid() { 34 | t=$(mktemp /tmp/groups.XXXXXX) 35 | for x in $(ls $FSDIR/group.*); do 36 | sed "/$1/d" "$x" >"$t" 37 | mv "$t" "$x" 38 | done 39 | rm -f "$t" 40 | } 41 | 42 | # cleans group ($1) from (in)active files 43 | clean_status() { 44 | t=$(mktemp /tmp/groups.XXXXXX) 45 | sed "/$1/d" $FSDIR/active >"$t" 46 | mv "$t" $FSDIR/active 47 | sed "/$1/d" $FSDIR/inactive >"$t" 48 | mv "$t" $FSDIR/inactive 49 | } 50 | 51 | # shows all the windows in group ($1) 52 | map_group() { 53 | # safety 54 | if ! grep -q $1 < $FSDIR/all; then 55 | echo "Group doesn't exist" 56 | exit 1 57 | fi 58 | 59 | # clean statuses 60 | clean_status $1 61 | # add to active 62 | echo $1 >> $FSDIR/active 63 | 64 | # loop through group and map windows 65 | xargs mapw -m <$FSDIR/group.$1 66 | } 67 | 68 | # hides all the windows in group ($1) 69 | unmap_group() { 70 | # safety 71 | if ! grep -q $1 < $FSDIR/all; then 72 | echo "Group doesn't exist" 73 | exit 1 74 | fi 75 | 76 | # clean statuses 77 | clean_status $1 78 | # add to inactive 79 | echo $1 >> $FSDIR/inactive 80 | 81 | # loop through group and unmap windows 82 | while read line; do 83 | mapw -u $line 84 | done < $FSDIR/group.$1 85 | } 86 | 87 | # assigns WID ($1) to the group ($2) 88 | set_group() { 89 | #so that neither grep nor ls in clean_wid complain 90 | #when group.$2 does not exist 91 | touch $FSDIR/group.$2 92 | 93 | # make sure we've no duplicates 94 | clean_wid $1 95 | clean_status $2 96 | 97 | # insert WID into new group if not already there 98 | grep -q $1 < $FSDIR/group.$2 || \ 99 | echo $1 >> $FSDIR/group.$2 100 | 101 | # if we can't find the group add it to groups and make it active 102 | grep -q $2 < $FSDIR/all || \ 103 | echo $2 >> $FSDIR/all && \ 104 | echo $2 >> $FSDIR/active 105 | 106 | # map WID if group is active 107 | grep -q $2 < $FSDIR/active && \ 108 | mapw -m $1 109 | 110 | # unmap WID if group is inactive 111 | grep -q $2 < $FSDIR/inactive && \ 112 | mapw -u $1 113 | } 114 | 115 | # toggles visibility state of all the windows in group ($1) 116 | toggle_group() { 117 | # safety 118 | if ! grep -q $1 < $FSDIR/all; then 119 | echo "Group doesn't exist" 120 | return 121 | fi 122 | 123 | # search through active groups first 124 | grep -q $1 < $FSDIR/active && \ 125 | unmap_group $1 && \ 126 | return 127 | 128 | # search through inactive groups next 129 | grep -q $1 < $FSDIR/inactive && \ 130 | map_group $1 && \ 131 | return 132 | } 133 | 134 | # removes all the unexistent WIDs from groups 135 | # removes all group files that don't exist 136 | # removes from 'all' file all groups that don't exist 137 | cleanup_everything() { 138 | # clean WIDs that don't exist 139 | # using `cat` instead of `<` because error suppression 140 | cat $FSDIR/group.* 2>/dev/null | while read wid; do 141 | wattr $wid || \ 142 | clean_wid $wid 143 | done 144 | 145 | # clean group files that are empty 146 | for file in $FSDIR/group.*; do 147 | # is the group empty? 148 | if [ ! -s $file ]; then 149 | rm -f $file 150 | fi 151 | done 152 | 153 | # remove groups that don't exist from 'all' 154 | while read line; do 155 | if [ ! -f $FSDIR/group.$line ]; then 156 | t=$(mktemp /tmp/groups.XXXXXX) 157 | sed "/$line/d" $FSDIR/all >"$t" 158 | mv "$t" $FSDIR/all 159 | clean_status $line 160 | fi 161 | done < $FSDIR/all 162 | } 163 | 164 | # actual run logic (including arguments and such) 165 | 166 | # check $FSDIR exists 167 | test -d $FSDIR || mkdir -p $FSDIR 168 | 169 | # touch all the files 170 | test -f $FSDIR/active || :> $FSDIR/active 171 | test -f $FSDIR/inactive || :> $FSDIR/inactive 172 | test -f $FSDIR/all || :> $FSDIR/all 173 | 174 | cleanup_everything 175 | 176 | # getopts yo 177 | while getopts "hc:Cs:t:m:M:u:U" opt; do 178 | case $opt in 179 | h) 180 | usage 181 | ;; 182 | c) 183 | clean_wid $OPTARG 184 | mapw -m $OPTARG 185 | break 186 | ;; 187 | C) 188 | cleanup_everything 189 | break 190 | ;; 191 | s) 192 | set_group $OPTARG $(eval echo "\$$OPTIND") 193 | break 194 | ;; 195 | t) 196 | toggle_group $OPTARG 197 | break 198 | ;; 199 | m) 200 | map_group $OPTARG 201 | break 202 | ;; 203 | M) 204 | for file in $FSDIR/group.*; do 205 | group=${file##*.} 206 | unmap_group $group 207 | done 208 | map_group $OPTARG 209 | break 210 | ;; 211 | u) 212 | unmap_group $OPTARG 213 | break 214 | ;; 215 | U) 216 | for file in $FSDIR/group.*; do 217 | group=${file##*.} 218 | unmap_group $group 219 | done 220 | break 221 | ;; 222 | esac 223 | done 224 | -------------------------------------------------------------------------------- /killwa/Makefile: -------------------------------------------------------------------------------- 1 | include config.mk 2 | 3 | HDR = arg.h util.h 4 | SRC = \ 5 | killwa.c 6 | 7 | OBJ = $(SRC:.c=.o) 8 | BIN = $(SRC:.c=) 9 | 10 | .POSIX: 11 | .SUFFIXES: .1 .1.gz 12 | 13 | all: binutils 14 | 15 | binutils: $(BIN) 16 | 17 | $(OBJ): $(HDR) util.o 18 | 19 | .o: 20 | @echo "LD $@" 21 | @$(LD) $< util.o -o $@ $(LDFLAGS) 22 | 23 | .c.o: 24 | @echo "CC $<" 25 | @$(CC) -c $< -o $@ $(CFLAGS) 26 | 27 | install: $(BIN) 28 | mkdir -p $(DESTDIR)$(PREFIX)/bin/ 29 | cp -f $(BIN) $(DESTDIR)$(PREFIX)/bin/ 30 | 31 | uninstall: 32 | @echo "uninstalling binaries" 33 | @for util in $(BIN); do \ 34 | rm -f $(DESTDIR)$(PREFIX)/bin/$$util; \ 35 | done 36 | 37 | clean : 38 | rm -f $(OBJ) $(BIN) util.o 39 | -------------------------------------------------------------------------------- /killwa/arg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copy me if you can. 3 | * by 20h 4 | */ 5 | 6 | #ifndef ARG_H__ 7 | #define ARG_H__ 8 | 9 | extern char *argv0; 10 | 11 | /* use main(int argc, char *argv[]) */ 12 | #define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ 13 | argv[0] && argv[0][1]\ 14 | && argv[0][0] == '-';\ 15 | argc--, argv++) {\ 16 | char argc_;\ 17 | char **argv_;\ 18 | int brk_;\ 19 | if (argv[0][1] == '-' && argv[0][2] == '\0') {\ 20 | argv++;\ 21 | argc--;\ 22 | break;\ 23 | }\ 24 | for (brk_ = 0, argv[0]++, argv_ = argv;\ 25 | argv[0][0] && !brk_;\ 26 | argv[0]++) {\ 27 | if (argv_ != argv)\ 28 | break;\ 29 | argc_ = argv[0][0];\ 30 | switch (argc_) 31 | 32 | /* Handles obsolete -NUM syntax */ 33 | #define ARGNUM case '0':\ 34 | case '1':\ 35 | case '2':\ 36 | case '3':\ 37 | case '4':\ 38 | case '5':\ 39 | case '6':\ 40 | case '7':\ 41 | case '8':\ 42 | case '9' 43 | 44 | #define ARGEND }\ 45 | } 46 | 47 | #define ARGC() argc_ 48 | 49 | #define ARGNUMF(base) (brk_ = 1, estrtol(argv[0], (base))) 50 | 51 | #define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ 52 | ((x), abort(), (char *)0) :\ 53 | (brk_ = 1, (argv[0][1] != '\0')?\ 54 | (&argv[0][1]) :\ 55 | (argc--, argv++, argv[0]))) 56 | 57 | #define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ 58 | (char *)0 :\ 59 | (brk_ = 1, (argv[0][1] != '\0')?\ 60 | (&argv[0][1]) :\ 61 | (argc--, argv++, argv[0]))) 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /killwa/config.mk: -------------------------------------------------------------------------------- 1 | PREFIX = /usr 2 | 3 | CC = cc 4 | LD = $(CC) 5 | 6 | CFLAGS = -std=c99 -pedantic -Wall -Os 7 | LDFLAGS = -lxcb -lxcb-icccm 8 | -------------------------------------------------------------------------------- /killwa/killwa.c: -------------------------------------------------------------------------------- 1 | // Created by https://github.com/onodera-punpun 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "util.h" 12 | 13 | static xcb_connection_t *conn; 14 | 15 | static void usage(char *); 16 | 17 | static void 18 | usage (char *name) 19 | { 20 | fprintf(stderr, "usage: %s [wid...]\n", name); 21 | exit(1); 22 | } 23 | 24 | static 25 | xcb_atom_t xcb_atom_get(xcb_connection_t *conn, char *name) 26 | { 27 | xcb_intern_atom_cookie_t cookie = xcb_intern_atom(conn ,0, strlen(name), name); 28 | xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, cookie, NULL); 29 | 30 | if (!reply) 31 | return XCB_ATOM_STRING; 32 | 33 | return reply->atom; 34 | } 35 | 36 | void 37 | delete_window(xcb_window_t win) 38 | { 39 | xcb_client_message_event_t ev; 40 | 41 | ev.response_type = XCB_CLIENT_MESSAGE; 42 | ev.sequence = 0; 43 | ev.format = 32; 44 | ev.window = win; 45 | ev.type = xcb_atom_get(conn, "WM_PROTOCOLS"); 46 | ev.data.data32[0] = xcb_atom_get(conn, "WM_DELETE_WINDOW"); 47 | ev.data.data32[1] = XCB_CURRENT_TIME; 48 | 49 | xcb_send_event(conn, 0, win, XCB_EVENT_MASK_NO_EVENT, (char *)&ev); 50 | } 51 | 52 | int 53 | main(int argc, char **argv) 54 | { 55 | xcb_window_t win = 0; 56 | 57 | if (argc < 2) 58 | usage(argv[0]); 59 | 60 | init_xcb(&conn); 61 | 62 | /* assume remaining arguments are windows */ 63 | while (*argv) 64 | win = strtoul(*argv++, NULL, 16); 65 | 66 | xcb_icccm_get_wm_protocols_reply_t reply; 67 | unsigned int i = 0; 68 | bool got = false; 69 | 70 | if (xcb_icccm_get_wm_protocols_reply(conn, xcb_icccm_get_wm_protocols(conn, win, xcb_atom_get(conn, "WM_PROTOCOLS")), &reply, NULL)) { 71 | for (; i != reply.atoms_len; ++i) 72 | if ((got = reply.atoms[i] == xcb_atom_get(conn, "WM_DELETE_WINDOW"))) 73 | break; 74 | 75 | xcb_icccm_get_wm_protocols_reply_wipe(&reply); 76 | } 77 | 78 | if (got) 79 | delete_window(win); 80 | else 81 | xcb_kill_client(conn, win); 82 | 83 | xcb_flush(conn); 84 | 85 | kill_xcb(&conn); 86 | 87 | return 0; 88 | } 89 | -------------------------------------------------------------------------------- /killwa/util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "util.h" 7 | 8 | void 9 | init_xcb(xcb_connection_t **con) 10 | { 11 | *con = xcb_connect(NULL, NULL); 12 | if (xcb_connection_has_error(*con)) 13 | errx(1, "unable connect to the X server"); 14 | } 15 | 16 | void 17 | kill_xcb(xcb_connection_t **con) 18 | { 19 | if (*con) 20 | xcb_disconnect(*con); 21 | } 22 | 23 | void 24 | get_screen(xcb_connection_t *con, xcb_screen_t **scr) 25 | { 26 | *scr = xcb_setup_roots_iterator(xcb_get_setup(con)).data; 27 | if (*scr == NULL) 28 | errx(1, "unable to retrieve screen informations"); 29 | } 30 | 31 | int 32 | exists(xcb_connection_t *con, xcb_window_t w) 33 | { 34 | xcb_get_window_attributes_cookie_t c; 35 | xcb_get_window_attributes_reply_t *r; 36 | 37 | c = xcb_get_window_attributes(con, w); 38 | r = xcb_get_window_attributes_reply(con, c, NULL); 39 | 40 | if (r == NULL) 41 | return 0; 42 | 43 | free(r); 44 | return 1; 45 | } 46 | 47 | int 48 | mapped(xcb_connection_t *con, xcb_window_t w) 49 | { 50 | int ms; 51 | xcb_get_window_attributes_cookie_t c; 52 | xcb_get_window_attributes_reply_t *r; 53 | 54 | c = xcb_get_window_attributes(con, w); 55 | r = xcb_get_window_attributes_reply(con, c, NULL); 56 | 57 | if (r == NULL) 58 | return 0; 59 | 60 | ms = r->map_state; 61 | 62 | free(r); 63 | return ms == XCB_MAP_STATE_VIEWABLE; 64 | } 65 | 66 | int 67 | ignore(xcb_connection_t *con, xcb_window_t w) 68 | { 69 | int or; 70 | xcb_get_window_attributes_cookie_t c; 71 | xcb_get_window_attributes_reply_t *r; 72 | 73 | c = xcb_get_window_attributes(con, w); 74 | r = xcb_get_window_attributes_reply(con, c, NULL); 75 | 76 | if (r == NULL) 77 | return 0; 78 | 79 | or = r->override_redirect; 80 | 81 | free(r); 82 | return or; 83 | } 84 | 85 | int 86 | get_windows(xcb_connection_t *con, xcb_window_t w, xcb_window_t **l) 87 | { 88 | uint32_t childnum = 0; 89 | xcb_query_tree_cookie_t c; 90 | xcb_query_tree_reply_t *r; 91 | 92 | c = xcb_query_tree(con, w); 93 | r = xcb_query_tree_reply(con, c, NULL); 94 | if (r == NULL) 95 | errx(1, "0x%08x: no such window", w); 96 | 97 | *l = malloc(sizeof(xcb_window_t) * r->children_len); 98 | memcpy(*l, xcb_query_tree_children(r), 99 | sizeof(xcb_window_t) * r->children_len); 100 | 101 | childnum = r->children_len; 102 | 103 | free(r); 104 | return childnum; 105 | } 106 | -------------------------------------------------------------------------------- /killwa/util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H__ 2 | #define UTIL_H__ 3 | 4 | void init_xcb(xcb_connection_t **); 5 | void kill_xcb(xcb_connection_t **); 6 | 7 | void get_screen(xcb_connection_t *, xcb_screen_t **); 8 | int get_windows(xcb_connection_t *, xcb_window_t, xcb_window_t **); 9 | 10 | int exists(xcb_connection_t *, xcb_window_t); 11 | int mapped(xcb_connection_t *, xcb_window_t); 12 | int ignore(xcb_connection_t *, xcb_window_t); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /rainbow.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # z3bra - 2015 (c) wtfpl 4 | # make the current window "rainbowish"... Awesome idea from xero@nixers.net ! 5 | 6 | FREQ=${FREQ:-0.1} 7 | COLORS="888888 8064cc 6480cc 64cccc 80cc64 cccc64 cc6464" 8 | CUR=$(pfw) 9 | 10 | while :; do 11 | for c in $COLORS; do 12 | chwb -c $c $(pfw) 13 | sleep $FREQ 14 | done 15 | done 16 | -------------------------------------------------------------------------------- /snap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # wmutils/contrib>: snap.sh, 9/12/15 kekler 4 | # snaps focued window to the left, right, top, or bottom edge 5 | 6 | usage() { 7 | echo "usage: $(basename $0) " >&2 8 | exit 1 9 | } 10 | 11 | # default values for gaps and master area 12 | TOP_PANEL=${PANEL:-20} 13 | GAP=${GAP:-5} 14 | 15 | # get current window id and its borderwidth 16 | PFW=$(pfw) 17 | BW=$(wattr b $PFW) 18 | 19 | # get root window's size 20 | ROOT=$(lsw -r) 21 | SW=$(wattr w $ROOT) 22 | SH=$(wattr h $ROOT) 23 | 24 | # calculate usable screen size (without borders and gaps) 25 | SH=$((SH - TOP_PANEL)) 26 | 27 | snap_up() 28 | { 29 | wtp $GAP $((GAP + TOP_PANEL)) $((SW - 2*GAP - 2*BW)) $((SH/2 - 2*BW - GAP - GAP/2)) $PFW 30 | } 31 | 32 | snap_right() 33 | { 34 | wtp $((SW - SW/2 + GAP/2)) $((GAP + TOP_PANEL)) $((SW/2 - 2*BW - GAP - GAP/2)) $((SH - 2*BW - 2*GAP)) $PFW 35 | } 36 | 37 | snap_down() 38 | { 39 | wtp $GAP $((SH - SH/2 + GAP/2 + TOP_PANEL)) $((SW - 2*GAP - 2*BW)) $((SH/2 - 2*BW - GAP - GAP/2)) $PFW 40 | } 41 | 42 | snap_left() 43 | { 44 | wtp $GAP $((GAP + TOP_PANEL)) $((SW/2 - 2*BW - GAP - GAP/2)) $((SH - 2*BW - 2*GAP)) $PFW 45 | } 46 | 47 | HSW=$((SW/2 - 2*BW - GAP - GAP/2)) 48 | HSH=$((SH/2 - 2*BW - GAP - GAP/2)) 49 | 50 | snap_tr() 51 | { 52 | wtp $((SW - SW/2 + GAP/2)) $((GAP + TOP_PANEL)) $HSW $HSH $PFW 53 | } 54 | 55 | snap_br() 56 | { 57 | wtp $((SW - SW/2 + GAP/2)) $((SH - SH/2 + GAP/2 + TOP_PANEL)) $HSW $HSH $PFW 58 | } 59 | 60 | snap_tl() 61 | { 62 | wtp $GAP $((GAP + TOP_PANEL)) $HSW $HSH $PFW 63 | } 64 | 65 | snap_bl() 66 | { 67 | wtp $GAP $((SH - SH/2 + GAP/2 + TOP_PANEL)) $HSW $HSH $PFW 68 | } 69 | 70 | case $1 in 71 | h|a|west|left) snap_left ;; 72 | j|s|south|down) snap_down ;; 73 | k|w|north|up) snap_up ;; 74 | l|d|east|right) snap_right ;; 75 | tr|northeast) snap_tr ;; 76 | br|southeast) snap_br ;; 77 | tl|northwest) snap_tl ;; 78 | bl|southwest) snap_bl ;; 79 | esac 80 | 81 | -------------------------------------------------------------------------------- /switch_grid.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright (c) 2015 Greduan , licensed under the WTFPL license 4 | # Credit where credit is due, the grid algorithm was written by z3bra 5 | # 6 | # When used puts all the windows in a grid and when you focus one of the windows 7 | # it puts all the windows back to their original location and focuses the window 8 | # you switched to. 9 | # depends on: wew focus.sh 10 | 11 | TEMP=$(mktemp) && wattr xywhi $(lsw) > $TEMP 12 | NB=$(wc -l < $TEMP) # NB as in NumBer of windows 13 | 14 | # just safety 15 | if [ $NB -eq 1 ]; then 16 | exit 17 | fi 18 | 19 | # user defined 20 | BW=${BW:-4} # width of your borders 21 | GAP=${GAP:-20} # gap between windows 22 | 23 | # get monitor dimensions 24 | ROOT=$(lsw -r) 25 | SW=$(wattr w $ROOT) 26 | SH=$(wattr h $ROOT) 27 | # reduce screen useable screen area to improve later expressions 28 | SW=$(( SW - GAP - 2*BW )) 29 | SH=$(( SH - GAP - 2*BW )) 30 | 31 | # calculate the size of the grid using the square root of the number of windows 32 | ROWS=$(echo "sqrt($NB)" | bc) 33 | COLS=$ROWS 34 | 35 | # FOLLOWING WAS WRITTEN BY Z3BRA, don't give me credit for this awesome logic 36 | # for each row... 37 | for r in `seq 1 $ROWS`; do 38 | 39 | # .. if we're on the last row, display all the remaining windows 40 | # eg: if we have 12 windows, the square root would be 3 (truncated). 41 | # so the script would draw a 3x3 grid. This would leave 3 windows apart. To 42 | # avoid this, we set the number of columns of the last row as 43 | # 44 | # 12 - 3 * (3-1) 45 | # => 12 - 3 * 2 46 | # => 12 - 6 47 | # == 6 48 | # so we will have 6 windows on the last row, instead of 3. 49 | # This do not lead to the best looking grid, I know (the best one would be 50 | # 3x4), but it's the simplest algo I've found. Don't forget we're playing 51 | # with shell scripts here, not matlab.. 52 | test $r -eq $ROWS && COLS=$(( NB - ROWS * (ROWS-1) )) 53 | 54 | # for each column of each row.. 55 | for c in `seq 1 $COLS`; do 56 | 57 | # exit if we don't have other windows to display 58 | test $(( (r-1)*r + c )) -gt $NB && break 59 | 60 | # heigh of windows (total heigh minus gaps and borders) 61 | H=$(( SH/ROWS - GAP - BW )) 62 | # same for width 63 | W=$(( SW/COLS - GAP - BW )) 64 | 65 | # and the tricky part.. 66 | # The X offset is the width * the actual column (starting from 0) + the 67 | # gaps/borders multiplied by the column number (draw it on a sheet of 68 | # paper like me, it will make much more sense! 69 | X=$(( W * (c-1) + c*(GAP + BW) )) 70 | # same for the Y offset 71 | Y=$(( H * (r-1) + r*(GAP + BW) )) 72 | 73 | # finally, teleport the window to the place we just calculated. 74 | # the sed trick is used to get the corresponding line number in the file 75 | # holding the window infos. 76 | wtp $X $Y $W $H $(sed "$(( (r-1)*r + c ))p;d" $TEMP | cut -d\ -f5) 77 | done 78 | done 79 | # END Z3BRA 80 | 81 | # listen to wew for our desired event 82 | wew -m 4 | while IFS=: read ev wid; do 83 | if [ $ev -eq 4 ]; then 84 | while read line; do 85 | wtp $line 86 | done < $TEMP 87 | focus.sh $wid 88 | killall wew 89 | fi 90 | done 91 | 92 | # cleanup 93 | rm $TEMP 94 | -------------------------------------------------------------------------------- /tile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # z3bra - 2014 (c) wtfpl 4 | # arrange windows in a tiled pattern 5 | 6 | # default values for gaps and master area 7 | PANEL=${PANEL:-0} 8 | GAP=${GAP:-20} 9 | MASTER=${MASTER:-900} 10 | 11 | # get current window id and its borderwidth 12 | PFW=$(pfw) 13 | BW=$(wattr b $PFW) 14 | 15 | # get root window's size (beware, multi-head setups...) 16 | ROOT=$(lsw -r) 17 | SW=$(wattr w $ROOT) 18 | SH=$(wattr h $ROOT) 19 | 20 | # get the number of windows to put in the stacking area 21 | MAX=$(lsw|grep -v $PFW|wc -l) 22 | 23 | # calculate usable screen size (without borders and gaps) 24 | SW=$((SW - GAP - 2*BW)) 25 | SH=$((SH - GAP - PANEL)) 26 | 27 | Y=$((0 + GAP + PANEL)) 28 | # put current window in master area 29 | wtp $GAP $Y $((MASTER - GAP - 2*BW)) $((SH - GAP - 2*BW)) $PFW 30 | 31 | # and now, stack up all remaining windows on the right 32 | X=$((MASTER + GAP)) 33 | W=$((SW - MASTER - GAP)) 34 | H=$((SH / MAX - GAP - 2*BW)) 35 | 36 | for wid in $(lsw|grep -v $PFW); do 37 | wtp $X $Y $W $H $wid 38 | Y=$((Y + H + GAP + 2*BW)) 39 | done 40 | -------------------------------------------------------------------------------- /underneath.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # wildefyr - 2016 (c) MIT 4 | # print window id directly underneath mouse 5 | 6 | # fuck sh 7 | intCheck() { 8 | test $1 -ne 0 2> /dev/null 9 | test $? -ne 2 || return 1 10 | } 11 | 12 | # find current mouse location or check input for XY coordinates 13 | test $# -eq 0 && { 14 | pointerX="$(wmp | cut -d\ -f 1)" 15 | pointerY="$(wmp | cut -d\ -f 2)" 16 | } || { 17 | intCheck $1 && pointerX=$1 || return 1 18 | intCheck $2 && pointerY=$2 || return 1 19 | } 20 | 21 | # start from the currently highest stacked window 22 | for wid in $(lsw | tac); do 23 | windowX="$(wattr x $wid)" 24 | windowY="$(wattr y $wid)" 25 | 26 | # we won't get a match if the left and top edges are greater than X or Y 27 | test "$windowX" -gt "$pointerX" && continue 28 | test "$windowY" -gt "$pointerY" && continue 29 | 30 | windowW="$(wattr w $wid)" 31 | windowH="$(wattr h $wid)" 32 | 33 | # we won't get a match if the right and bottom edges are less than X or Y 34 | test "$((windowX + windowW))" -lt "$pointerX" && continue 35 | test "$((windowY + windowH))" -lt "$pointerY" && continue 36 | 37 | # print match! 38 | printf '%s\n' "$wid" 39 | unset -v wid 40 | exit 41 | done 42 | -------------------------------------------------------------------------------- /wmenu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # fyr | 2019 (c) MIT 4 | # dmenu wrapper to select windows based on their window title 5 | # use xprop -id $wid WM_NAME or WM_CLASS for better matching 6 | 7 | LINES=$(lsw | wc -l) 8 | 9 | wid=$(\ 10 | for wid in $(lsw); do 11 | printf '%s\n' "$wid | $(wname $wid)" 12 | done | dmenu -name "wmenu" -l $LINES -p "Window:" | cut -d\ -f 1) 13 | 14 | test -n "$wid" && focus.sh "$wid" 15 | -------------------------------------------------------------------------------- /workspace.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Constructs and groups windows into workspaces to switch between using your favorite keybinder. 3 | 4 | NUM_WS=9 5 | 6 | help() { 7 | cat << EOF 8 | usage: $(basename $0) [-hinp] [-g ws_num] [-m ws_num] 9 | -h: Displays this message 10 | -i: Initialize workspaces. Should be called once in a startup script. 11 | -n: Move up one workspace 12 | -p: Move down one workspace 13 | -g, : go to the workspace specified by 14 | -m, : move the currently focused window to the worskpace specified by 15 | EOF 16 | exit 1 17 | } 18 | # Initializes us in workspace 0. 19 | ws_init() { 20 | mkdir -p /tmp/workspaces/ 21 | i=0 22 | while [ $i -le $NUM_WS ]; do 23 | :> /tmp/workspaces/ws"$i" 24 | i=$(expr $i + 1) 25 | done 26 | echo 0 > /tmp/workspaces/curr 27 | } 28 | # Saves all mapped windows to the current workspace. 29 | save_ws() { 30 | curr=$(cat /tmp/workspaces/curr) 31 | lsw > /tmp/workspaces/ws"$curr" 32 | } 33 | move_to_ws() { 34 | ws_num=$1 35 | if [ $ws_num -gt $NUM_WS ] || [ $ws_num -lt 0 ]; then 36 | echo "Workspace not found" 37 | return 38 | fi 39 | curr_ws=$(cat /tmp/workspaces/curr); 40 | if [ $ws_num == $curr_ws ]; then 41 | # same workspace. ignore flicker 42 | return 43 | fi 44 | save_ws 45 | curr_windows=$(lsw) 46 | if [ "$curr_windows" ]; then 47 | mapw -u $(lsw) 48 | fi 49 | new_windows="$(cat /tmp/workspaces/ws$ws_num)" 50 | if [ "$new_windows" ]; then 51 | mapw -m $new_windows 52 | fi 53 | echo $ws_num > /tmp/workspaces/curr 54 | } 55 | next_ws() { 56 | # Get what ws we're currently in. 57 | curr=$(cat /tmp/workspaces/curr) 58 | curr=$(expr $curr + 1) 59 | 60 | # Take care of loopback. 61 | if [ $curr -gt $NUM_WS ]; then 62 | curr=0 63 | fi 64 | 65 | move_to_ws $curr 66 | } 67 | prev_ws() { 68 | # Get what ws we're currently in. 69 | curr=$(cat /tmp/workspaces/curr) 70 | curr=$(expr $curr - 1) 71 | 72 | # Take care of loopback. 73 | if [ $curr -lt 0 ]; then 74 | curr=$NUM_WS 75 | fi 76 | 77 | move_to_ws $curr 78 | } 79 | move_focused_window() { 80 | ws_num=$1 81 | if [ $ws_num -gt $NUM_WS ] || [ $ws_num -lt 0 ]; then 82 | echo "Workspace not found" 83 | return 84 | fi 85 | wid=$(pfw) 86 | curr_ws=$(cat /tmp/workspaces/curr); 87 | if [ $ws_num != $curr_ws ]; then 88 | pfw >> /tmp/workspaces/ws"$1" 89 | mapw -u $wid 90 | fi 91 | } 92 | while getopts ":m:g:npi" opt; do 93 | case $opt in 94 | n) 95 | next_ws;; 96 | p) 97 | prev_ws;; 98 | g) 99 | move_to_ws $OPTARG;; 100 | m) 101 | move_focused_window $OPTARG;; 102 | i) 103 | ws_init;; 104 | \?) 105 | help;; 106 | esac 107 | done 108 | --------------------------------------------------------------------------------