#!/usr/bin/env zsh # terminal to launch when splitting # undefined window class. # first choice urxvt fallback xterm. terminal="${commands[urxvt]:-xterm}" typeset -a allowed_actions=( xbindkeys start end ) if [ ${allowed_actions[(I)$1]} -le 0 ]; then cat <<-EOF i3winsplit.zsh ============== Your scientists were so preoccupied with whether or not they could, they didn’t stop to think if they should. - Jurassic Park Updates: ======== https://gist.github.com/DerBunman/2fe23d26d4a79bb4fe4cab1e243062ee/ About: ====== A tool which enables you to cut thru your i3 windows to split them. Whatever the reason you feel like you need to do it. I feel you. When I first had this idea it was a gamechanger for migrating back from keyboard centric user input to a more mouse oriented approach. While I soon realized that none of this made sense, I somehow wanted to see and feel how this would work. So well. Behold its glory: https://streamable.com/016h2 Known problems: =============== * Sometimes i3 will reply with this message and split the window in the middle: "ERROR: Failed to find appropriate tiling containers for resize operation" * Some applications don't want to be resized. vim-gtk3 for example just resizes never like it should, but sometimes it resizes some more windows while itd at it. * However, I don't plan to use this script, so I didn't investigate any of these issues. Dependencies: ============= The following binaries have to be in your path: - xbindkeys - xdotool - i3-msg - zsh (tested with 5.5.1, but should work with >=5.4.2) - tee - urxvt or xterm - notify-send (optional. else output will only appear on stdout) And, i3 has to be running with a few windows to tear down. Allowed actions: ================ - xbindkeys: Starts the xbindkeys service which binds control + mouse button 3 (rmc) to the start and end trigger. This is the only action you should call. The other actions are said start and end trigger which will be called on control+mouse button 3 (rmc) trgger (start) and release (end). So, this is the command you should run: ======================================= ${0} xbindkeys EOF exit 1 elif [ "$1" = "xbindkeys" ]; then xbindkeys --verbose --nodaemon --file <(<<-EOF "$0 start" control + b:3 "$0 end" release+control + b:3 EOF ) exit elif [ "$1" = "start" ]; then xdotool getmouselocation >~/.i3winsplit.tmp exit fi # use notify-send when in path` NOTIFY_SEND="${commands[notify-send]:-cat}" # $1 = end setopt SH_WORD_SPLIT typeset -A after=( ${$(xdotool getmouselocation)//:/ } ) typeset -A before=( ${$(<~/.i3winsplit.tmp)//:/ } ) # which command to send i3 # depending on cut direction. typeset -A splitcmd=( x "split v" y "split h" ) # everything not defined here will spawn # a new $TERM instance instead typeset -A class_cmds=( # gvim doesn't like to be split # gvim "gvim" urxvt "urxvtcd" firefox "xdotool key --window ${before[window]} control+n" ) { # The following block will be sent to stdout and when in path notify-send # abort when the mouse motion crossed more than one window [ $after[window] -ne $before[window] ] && { echo "[ $after[window] -ne $before[window] ]" echo "ERROR: window ids are different." exit } zmodload zsh/mathfunc typeset -A diff diff[x]=$(( $before[x] - $after[x] )) diff[x_abs]=$(( sqrt($diff[x] * $diff[x]) )) diff[y]=$(( $before[y] - $after[y] )) diff[y_abs]=$(( sqrt($diff[y] * $diff[y]) )) (( $diff[x_abs] > $diff[y_abs] )) \ && direction="x" \ || direction="y" class=${(@)${$(xprop -id ${before[window]} WM_CLASS)}[4]//'"'/} echo direction $splitcmd[$direction] [ $direction = x ] \ && { axis=y; size=XWSHEIGHT } \ || { axis=x; size=XWSWIDTH } offset_screen=$(( ( $before[$axis] + $after[$axis] ) /2 )) eval $(xdotool getwindowgeometry --prefix XWS --shell $before[window]) offset=$(( $offset_screen - ${(P)$(echo "XWS${axis:u}")} )) # remaining percent of the window # echo "PERC $(( $offset.0 / ${(P)size} *100 ))" xdotool windowactivate $before[window] i3-msg "${splitcmd[$direction]}"; sleep .5 eval "${class_cmds[$class:l]:-$terminal}" &| xdotool search --onlyvisible --pid $! [ "${class:l}" = "firefox" ] && sleep .5 xdotool windowactivate $before[window] [ $direction = x ] \ && i3-msg "resize set height $offset px" \ || i3-msg "resize set width $offset px" } 2>&1 | tee >("$NOTIFY_SEND" "$(cat -)")