#
# SXHKD module with BPSWM-specific commands
#
# ~/.config/sxhkd/sxhkdrc_bspc
#
# Other modules should be available at ~/.config/sxhkd/
#

# Description
# ===========
#
# Partial configuration file for the Simple X Hotkey Daemon.  Meant to
# work with `bspwm`.  This is part of my dotfiles, which are managed
# with GNU Stow.  See https://gitlab.com/protesilaos/dotfiles
#
# LARGE PORTIONS OF THIS FILE INVOKE MY SHELL SCRIPTS.  Do not blindly
# copy-paste from here!
#
# Last full review on 2019-06-18



# Close {or kill} currently focused node
super + {_,shift + }q
	bspc node -{c,k}

# Focus/swap nodes/desktops
# -------------------------

# Focus the node in the given direction.
super + {h,j,k,l}
	bspc node -f {west,south,north,east}

# Swap focused window with the one in the given direction.  If there is
# no window in that direction, a receptacle will be created.  Inputting
# any direction again will move the focused window to the position of
# the receptacle.  This will effectively remove it from the current
# layout, creating a more fluid workflow that does not constrain you to
# the tree of the tiling scheme.
#
# IF you only ever want to move a window into an existing one and avoid
# the added features of the scripted behaviour, use the following
# command instead:
#
# 	bspc node -s {west,south,north,east}
#
# TIP after breaking free from the layout, you can use --balance to
# spread out the nodes (see the key chord further down).  You may also
# --rotate the tree.
#
# bspwm_smart_move is part of my dotfiles (link in the description).
super + shift + {h,j,k,l}
	bspwm_smart_move {west,south,north,east}

# Dynamic desktop behaviour:  this invokes a script of mine that
# implements dynamic desktops (see dotfiles).  If the given desktop
# number is missing, it is created on the spot.  Empty desktops are then
# removed automatically.
#
#
# super + <number> = focus the given desktop
# super + shift + <number> = send focused window to given desktop
# super + ctrl + shift + <number> = as above, but send to next monitor
# super + ctrl + <number> = switches the active (visible) desktop of the
# unfocused monitor, without focusing it (focus stays on the current
# desktop/monitor).  It also inserts a receptacle, meaning that the next
# created window will go there without calling focus to itself (see my
# bspwm_external_rules).
#
#
# Commands for receptacles are defined in the "advanced operations"
# section.
#
# NOTE the leading `;` runs this command asynchronously.
super + {_,shift + ,ctrl + shift +,ctrl +}{0-9}
	;bspwm_dynamic_desktops {--df,--ns,--nm,--da} {0-9}

# With `alt` focus {next,previous} window in the current desktop.  With
# `super` focus {next,previous} desktop in the current monitor.
# Cyclical behaviour.  Move in the opposite direction while also holding
# down `shift`.
{super,alt} + {_,shift +} Tab
	bspc {desktop,node} -f {next,prev}.local

# Switch to the {next,previous} monitor.  This brings focus to that
# monitor's active desktop.
super + {_,shift + }comma
	bspc monitor -f {next,prev}

# Switch to last active {desktop,node}.  With super move to the last
# active desktop.  With alt move to the last active node. "grave" is
# positioned above Tab in QWERTY layouts.
{super,alt} + grave
	bspc {desktop,node} -f last

# Expand or contract node in the given direction.
#
# For floating windows one can use Alt + Right Mouse click to
# drag/resize in the given direction (this works in other DEs as well,
# such as MATE and Xfce).  I have not set the script to resize floating
# windows because: (i) I do not use them and if I do I do not want to
# change their dimensions, and (ii) to keep the script concise.
super + ctrl + {h,j,k,l}
	own_script_bspwm_node_resize {west,south,north,east} 50

# Move a floating window with the arrow keys (else use Alt + Left Mouse
# Click to drag).  I seldom use floating windows.
super + shift + {Left,Down,Up,Right}
	bspc node -v {-20 0,0 20,0 -20,20 0}

# Swap current window with the biggest one in the present
# desktop.
super + shift + b
	bspc node -s biggest.local

# Preselect the direction or insert again to cancel the preselection.
# This enters the manual tiling mode that splits the currently focused
# window.
super + alt + {h,j,k,l}
	bspc node --presel-dir '~{west,south,north,east}'

# Preselect the ratio.  The default value is 0.5, defined in `bspwmrc`.
super + alt + {1-9}
	bspc node -o 0.{1-9}

# Node layout state and flags
# ---------------------------

# Toggle monocle layout (maximise focused node).  If you use `-l
# monocle` you lose the toggle functionality.
super + m
	bspc desktop -l next

# Toggle tiled, pseudo_tiled, floating, fullscreen view.  Tiled is what
# is used by default.  Pseudo-tiled presents the window in its original
# dimensions inside the split it would normally occupy as a tile (I have
# never found a practical use for this).  Floating windows have their
# own dimensions and can be stacked on top of each other.
#
# NOTE the presence of the tilde ~.  If current state matches the given
# state, then the argument is interpreted as the last state.
super + {t,shift + space,space,f}
 	bspc node -t ~{tiled,pseudo_tiled,floating,fullscreen}

# Set the node flags.  Locked is a node that cannot be closed with `bspc
# node --close`.  Marked is a specific selector that allows operating on
# the given item (such as sending the marked item to the newest
# preselection).  Sticky stays in the focused desktop.  Private tries to
# keep its dimensions and tiling position.
super + a ; {l,m,s,p}
	bspc node -g {locked,marked,sticky,private}

# Set the split ratio on demand.  Applies to the next window, or when
# using the next set of commands that equalise the tree or balance the
# parent node.
super + s ; {2,3,4}
	bspc config split_ratio { \
	0.50 && notify-send "BSPWM configuration" "Split ratio set to 0.50", \
	0.66 && notify-send "BSPWM configuration" "Split ratio set to 0.66", \
	0.75 && notify-send "BSPWM configuration" "Split ratio set to 0.75" \
	}

# Make node split ratios equal or balanced.  The former applies to the
# root of the tree to equalise all split ratios.  The latter applies to
# the parent node to even out manually-split windows.
super + s ; {e,b}
	bspc node {@/ --equalize,@parent --balance}

# Manipulate the workspace
# ------------------------

# Rotate all windows {counter-}clockwise by 90 degrees.
super + {_,shift + }r
	bspc node @/ --rotate {90,-90}

# Flip desktop: what is on the right/top moves to the left/bottom and
# vice versa.
super + alt + {_,shift + }r
	bspc node @/ --rotate {180,-180}

# Switch between gap presets.
super + shift + {y,u,i,o}
	bspc config window_gap {0,5,10,20}

# Dynamic gaps.
super + shift + bracket{left,right}
	bspc config -d focused window_gap "$(($(bspc config -d focused window_gap) {-,+} 5 ))"

# Advanced operations (receptacles and scripted actions)
# ------------------------------------------------------
# NOTE The behaviour of preselections and receptacles is regulated by my
# bspwm_external_rules command (which is called from within bspwmrc).

# Insert receptacle following the tiling scheme or preselection.
# Receptacles are empty frames that function as placeholders for
# windows.
#
# Whereas bspwm_smart_move (defined above) will automatically insert
# a receptacle in the given direction by splitting the entire viewport.
super + n ; r
	bspc node -i

# Insert the marked window (or focused if no marks) in the first
# matching receptacle (or preselection if no receptacles).
super + n ; i
	bspwm_smart_presel

# Kill/remove the first matching receptacle from the current desktop.
# With shift, kill them all at once.
super + n ; super + {_, shift + }r
	{ \
	bspc node 'any.leaf.!window.local' -k, \
	bspwm_multifaceted_operation --kill-all-receptacles \
	}

# Summon all marked windows across all desktops to the current desktop.
super + n ; m
	bspwm_multifaceted_operation --summon-all-marked

# Bring the windows of the given desktop to the current one.  With
# shift, send the current window to the given one.
super + n ; super + {_, shift + }{0-9}
	bspwm_multifaceted_operation {--bring-desktop-here,--send-desktop-there} {0-9}

# Close {or kill} all windows in the present desktop except the focused
# or any marked ones.
super + n ; super + {_,shift + }q
	bspwm_multifaceted_operation --{close,kill}-all-local-non-focused

# Assign the locked flag to all windows in the current desktop.  With
# shift, remove the locked flag from all.
super + n ; {_, shift + }l
	bspwm_multifaceted_operation {--lock-all-local,--unlock-all-local}


# Continuous input mode
# =====================
# EXPLAIN: The following key chords have a colon sign `:' before their
# last part.  By pressing the keys to the left of the colon, you can
# then pass the commands by only using the keys to the right of the
# colon.  This eliminates the need for repeated chord presses that
# involve the modifier keys.  Exit the mode with "Escape".
#
# NOTE: All of the following are mirrors of their afore-mentioned
# couterparts.

# Resize node in the given direction.
super + c : {h,j,k,l}
	own_script_bspwm_node_resize {west,south,north,east} 50

# Gaps presets.
super + c : {y,u,i,o}
	bspc config window_gap {0,5,10,20}

# Mode for dynamic gaps.
super + c : bracket{left,right}
	bspc config -d focused window_gap "$(($(bspc config -d focused window_gap) {-,+} 5 ))"

# Move floating windows with the arrow keys
super + c : {Left,Down,Up,Right}
	bspc node -v {-20 0,0 20,0 -20,20 0}
