Custom Window Snap
Many desktop environments for Linux have had window snapping (a.k.a window tiling or edge snapping) for a long time. It is a feature that I use very heavily, especially with hotkeys. I have been very dissatisfied with the way Linux desktop environments have handled this. They almost work the way I want, but not quite. I decided to take control of this.
Gosnap stands for "Good Old Snap" and replaces the window snapping/tiling feature on just about any Linux distro that uses the X Window System.
Download:
| Filename | Description | Size | Modified | Link |
|---|---|---|---|---|
|
gosnap |
The Gosnap bash script. | 6.4K | 7/31/2020 |
Installing:
wget https://beardedmaker.com/wiki/files/gosnap
chmod a+x gosnap
sudo cp gosnap /usr/binUsage:
Usage: gosnap [-L | -l | -R | -r | -M | -m | -N | -n | -d | -i]
-L snap left
-l snap left dynamically
-R snap right
-r snap right dynamically
-M maximize window
-m toggle maximize window
-N minimize window
-n minimize window dynamically
-d resize to default geometry (current default: 0,10,60,826,556
-i print window informationIf your desktop environment supports custom hotkeys, you can simply map any hotkey you want to execute this script with the appropriate parameter. For example:
- Snap Left:
gosnap -lSuper+Left - Snap Right:
gosnap -rSuper+Right - Snap Maximize:
gosnap -mSuper+Up - Snap Minimize:
gosnap -lSuper+Down
Note: To "snap dynamically" means to go to a "default" position as an intermediate step depending on the window's current position. For example, if I minimize the window using gosnap -n, but the window was previously maximized, I'd rather the window pop into the middle of the screen on the first invocation, and then when I minimize again I want the window to collapse to the taskbar. To just immediately minimize the window, use gosnap -N instead.
Gosnap Script:
#!/bin/bash
#
# GOSNAP: Good Old Snap
#
# Script to emulate the Aero Snap feature in Windows 7
# Use -L or -R flags to snap left or right
# Use -l or -r flags to snap dynamically (intermediate default position)
# Use -M flag to maximize
# Use -m flag to toggle maximized state
# Use -N flag to minimize
# Use -n flag to minimize dynamically (intermediate default position)
# Use -d flag to reset the window state to default
# Use -i flag to print window info to stdout
#
# wmctrl -e g,x,y,w,h (any value set to -1 won't be changed)
# g = window gravity (just set to 0)
# x = window x position
# y = window y position
# w = window width
# h = window height
#
# CONFIGURATION
# =============
# Default window geometry to use with -d
# Format: gravity, x_pos, y_pos, width, height)
window_default="0,10,60,826,556"
# The x position reported by xdotool when the window is snapped left/right.
# For some reason, snapping the window to 0,0 does not actually register as 0,0.
# Use the -i flag while the terminal window is snapped to the left or the right
# to get the values unique to your screen.
left_origin_x=10
right_origin_x=692
# Add this to the half screen width value for snapping left/right.
# If you see gaps on the left or right wien you snap, you may need to adjust this.
half_width_adjust_left=1
half_width_adjust_right=1
# Adjust these to avoid issues with gaps
snap_left_x=0
snap_left_y=0
snap_right_x=0
snap_right_y=1
# Use wmctrl bug avoidance
# For some reason, wmctrl intermittently snaps to the wrong position.
# Doing "snap_default && snap_maximize" before snapping left/right seems to fix it.
# The trade-off using this fix is that snapping left/right has a very slight delay.
wmctrl_bug_fix=true
function main
{
check_args $@
check_command wmctrl
check_command xdotool
get_window_state
while getopts ":LlRrMmNndi" opt;do
case $opt in
L)
snap_left
;;
l)
if $window_snapped_right;then
snap_default
else
snap_left
fi
;;
R)
snap_right
;;
r)
if $window_snapped_left;then
snap_default
else
snap_right
fi
;;
M)
snap_maximize
;;
m)
if $window_maximized;then
snap_default
else
snap_maximize
fi
;;
N)
snap_minimize
;;
n)
if $window_maximized || $window_snapped_left || $window_snapped_right;then
snap_default
else
snap_minimize
fi
;;
d)
snap_default
;;
i)
print_info
;;
\?)
echo "Invalid option: -$OPTARG" >&2
usage
exit 1
;;
esac
done
}
function check_args
{
if [[ "$@" == "" ]];then
usage
fi
}
function usage
{
echo
echo "Usage: gosnap [-L | -l | -R | -r | -M | -m | -N | -n | -d | -i]"
echo " -L snap left"
echo " -l snap left dynamically"
echo " -R snap right"
echo " -r snap right dynamically"
echo " -M maximize window"
echo " -m toggle maximize window"
echo " -N minimize window"
echo " -n minimize window dynamically"
echo " -d resize to default geometry (current default: $window_default"
echo " -i print window information"
echo
}
function check_command
{
if [[ ! -e $(which $1) ]];then
echo -e "\nERROR: $1 was not found on your system!\n"
# If we're on Ubuntu, use the c-n-f functionality
if [[ -x /usr/lib/command-not-found ]];then
python /usr/lib/command-not-found -- $1
elif [[ -x /usr/share/command-not-found ]];then
python /usr/share/command-not-found -- $1
fi
usage
exit 1
fi
}
function get_window_state
{
screen_width=`xdpyinfo | grep 'dimensions:' | cut -f 2 -d ':' | cut -f 1 -d 'x' | xargs`
window_id=$(xdotool getactivewindow)
window_id_hex=$(printf 0x%x $window_id)
window_properties=$(xprop -id $window_id)
window_state=$(echo $window_properties | grep "_NET_WM_STATE(ATOM)")
window_geometry=$(xdotool getwindowgeometry $window_id)
window_pos_x=$(echo $window_geometry | awk '{print $4}' | awk -F, '{print $1}')
window_pos_y=$(echo $window_geometry | awk '{print $4}' | awk -F, '{print $2}')
window_width=$(echo $window_geometry | awk '{print $8}' | awk -Fx '{print $1}')
window_height=$(echo $window_geometry | awk '{print $8}' | awk -Fx '{print $2}')
window_max_horiz=false
if [ $(echo $window_state | grep -o "_NET_WM_STATE_MAXIMIZED_HORZ") ];then
window_max_horiz=true
fi
window_max_vert=false
if [ $(echo $window_state | grep -o "_NET_WM_STATE_MAXIMIZED_VERT") ];then
window_max_vert=true
fi
window_maximized=false
if $window_max_horiz && $window_max_vert;then
window_maximized=true
fi
window_snapped_left=false
if $window_max_vert && [ $window_pos_x == $left_origin_x ];then
window_snapped_left=true
fi
window_snapped_right=false
if $window_max_vert && [ $window_pos_x == $right_origin_x ];then
window_snapped_right=true
fi
}
function snap_maximize
{
wmctrl -r :ACTIVE: -b add,maximized_vert,maximized_horz
}
function snap_minimize
{
xdotool getactivewindow windowminimize
}
function snap_default
{
wmctrl -r :ACTIVE: -b remove,maximized_vert,maximized_horz
wmctrl -r :ACTIVE: -e $window_default
}
function snap_left
{
if $wmctrl_bug_fix; then snap_default && snap_maximize;fi
halfw=$((($screen_width/2)+$half_width_adjust_left))
wmctrl -r :ACTIVE: -b remove,maximized_vert,maximized_horz &&\
wmctrl -r :ACTIVE: -b add,maximized_vert &&\
wmctrl -r :ACTIVE: -e 0,$snap_left_x,$snap_left_y,$halfw,-1
}
function snap_right
{
if $wmctrl_bug_fix; then snap_default && snap_maximize;fi
halfw=$((($screen_width/2)+$half_width_adjust_right+$snap_right_x))
wmctrl -r :ACTIVE: -b remove,maximized_vert,maximized_horz &&\
wmctrl -r :ACTIVE: -b add,maximized_vert &&\
wmctrl -r :ACTIVE: -e 0,$halfw,$snap_right_y,$halfw,-1
}
function print_info
{
echo "screen_width = $screen_width (half = $(($screen_width/2)))"
echo "Window $window_id ($window_id_hex)"
echo "$(echo $window_geometry | tr -d '\n' | grep -o 'Position.*' | xargs)"
echo "window_max_horiz = $window_max_horiz"
echo "window_max_vert = $window_max_vert"
echo "window_maximized = $window_maximized"
echo "window_snapped_left = $window_snapped_left"
echo "window_snapped_right = $window_snapped_right"
exit
}
main $@
# OTHER NOTES
#
# xdotool windowmove $(xdotool getactivewindow) 0 0
# xdotool windowmove $(xdotool getactivewindow) 100% 100%
# xdotool windowsize $(xdotool getactivewindow) 50% 100%
# xdotool getwindowgeometry $(xdotool getactivewindow)
#
# No window shadow: add this line to /etc/environment then relog
# MUFFIN_NO_SHADOWS=1
#