Difference between revisions of "Custom Window Snap"

From TheBeard Science Project Wiki
Jump to: navigation, search
(Gosnap Script)
 
(5 intermediate revisions by the same user not shown)
Line 1: Line 1:
 
 
__NOTOC__
 
__NOTOC__
 
 
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 <i>almost</i> work the way I want, but not quite. I decided to take control of this.
 
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 <i>almost</i> work the way I want, but not quite. I decided to take control of this.
  
Line 34: Line 32:
 
wget https://beardedmaker.com/wiki/files/gosnap
 
wget https://beardedmaker.com/wiki/files/gosnap
 
chmod a+x gosnap
 
chmod a+x gosnap
sudo cp gosnap /usr/bin
+
sudo mv gosnap /usr/bin
 
</source>
 
</source>
  
Line 48: Line 46:
 
   -N    minimize window
 
   -N    minimize window
 
   -n    minimize window dynamically
 
   -n    minimize window dynamically
   -d    resize to default geometry (current default: 0,10,60,826,556
+
   -d    resize to default geometry (current default: 0,10,60,826,556)
 
   -i    print window information
 
   -i    print window information
 
</source>
 
</source>
Line 60: Line 58:
 
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 <code>gosnap -n</code>, 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 <code>gosnap -N</code> instead.
 
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 <code>gosnap -n</code>, 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 <code>gosnap -N</code> instead.
  
== Gosnap Script: ==
+
== Gosnap Script ==
 
<source lang="sh" line>
 
<source lang="sh" line>
 
#!/bin/bash
 
#!/bin/bash
Line 122: Line 120:
 
get_window_state
 
get_window_state
 
 
while getopts ":LlRrMmNndi" opt;do
+
while getopts ":LlRrMmNndih" opt;do
 
case $opt in
 
case $opt in
 
L)
 
L)
Line 170: Line 168:
 
print_info
 
print_info
 
;;
 
;;
\?)
+
h)
 +
usage
 +
exit
 +
;;
 +
*)
 
echo "Invalid option: -$OPTARG" >&2
 
echo "Invalid option: -$OPTARG" >&2
 
usage
 
usage
Line 198: Line 200:
 
echo "  -N    minimize window"
 
echo "  -N    minimize window"
 
echo "  -n    minimize window dynamically"
 
echo "  -n    minimize window dynamically"
echo "  -d    resize to default geometry (current default: $window_default"
+
echo "  -d    resize to default geometry (current default: $window_default)"
 
echo "  -i    print window information"
 
echo "  -i    print window information"
 
echo
 
echo
Line 220: Line 222:
 
function get_window_state
 
function get_window_state
 
{
 
{
screen_width=`xdpyinfo | grep 'dimensions:' | cut -f 2 -d ':' | cut -f 1 -d 'x' | xargs`
+
screen_width=$(xdpyinfo | grep 'dimensions:' | cut -f 2 -d ':' | cut -f 1 -d 'x' | xargs)
 
window_id=$(xdotool getactivewindow)
 
window_id=$(xdotool getactivewindow)
 
window_id_hex=$(printf 0x%x $window_id)
 
window_id_hex=$(printf 0x%x $window_id)
Line 310: Line 312:
 
# No window shadow: add this line to /etc/environment then relog
 
# No window shadow: add this line to /etc/environment then relog
 
#  MUFFIN_NO_SHADOWS=1
 
#  MUFFIN_NO_SHADOWS=1
#
+
#</source>
</source>
 

Latest revision as of 22:39, 31 July 2020

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

Download

Installing

wget https://beardedmaker.com/wiki/files/gosnap
chmod a+x gosnap
sudo mv gosnap /usr/bin

Usage

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 information

If 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 -l Super+Left
  • Snap Right: gosnap -r Super+Right
  • Snap Maximize: gosnap -m Super+Up
  • Snap Minimize: gosnap -l Super+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

  1 #!/bin/bash
  2 #
  3 # GOSNAP: Good Old Snap
  4 #
  5 # Script to emulate the Aero Snap feature in Windows 7
  6 #   Use -L or -R flags to snap left or right
  7 #   Use -l or -r flags to snap dynamically (intermediate default position)
  8 #   Use -M flag  to maximize
  9 #   Use -m flag to toggle maximized state
 10 #   Use -N flag to minimize
 11 #   Use -n flag to minimize dynamically (intermediate default position)
 12 #   Use -d flag to reset the window state to default
 13 #   Use -i flag to print window info to stdout
 14 #
 15 # wmctrl -e g,x,y,w,h (any value set to -1 won't be changed)
 16 #   g = window gravity (just set to 0)
 17 #   x = window x position
 18 #   y = window y position
 19 #   w = window width
 20 #   h = window height
 21 #
 22 
 23 # CONFIGURATION
 24 # =============
 25 
 26 # Default window geometry to use with -d
 27 #   Format: gravity, x_pos, y_pos, width, height)
 28 window_default="0,10,60,826,556"
 29 
 30 # The x position reported by xdotool when the window is snapped left/right.
 31 #   For some reason, snapping the window to 0,0 does not actually register as 0,0.
 32 #   Use the -i flag while the terminal window is snapped to the left or the right
 33 #   to get the values unique to your screen.
 34 left_origin_x=10
 35 right_origin_x=692
 36 
 37 # Add this to the half screen width value for snapping left/right.
 38 #   If you see gaps on the left or right wien you snap, you may need to adjust this.
 39 half_width_adjust_left=1
 40 half_width_adjust_right=1
 41 
 42 # Adjust these to avoid issues with gaps
 43 snap_left_x=0
 44 snap_left_y=0
 45 snap_right_x=0
 46 snap_right_y=1
 47 
 48 # Use wmctrl bug avoidance
 49 #   For some reason, wmctrl intermittently snaps to the wrong position.
 50 #   Doing "snap_default && snap_maximize" before snapping left/right seems to fix it.
 51 #   The trade-off using this fix is that snapping left/right has a very slight delay.
 52 wmctrl_bug_fix=true
 53 
 54 function main
 55 {
 56 	check_args $@
 57 	check_command wmctrl
 58 	check_command xdotool
 59 	get_window_state
 60 	
 61 	while getopts ":LlRrMmNndih" opt;do
 62 		case $opt in
 63 		L)
 64 			snap_left
 65 			;;
 66 		l)
 67 			if $window_snapped_right;then
 68 				snap_default
 69 			else
 70 				snap_left
 71 			fi
 72 			;;
 73 		R)
 74 			snap_right
 75 			;;
 76 		r)
 77 			if $window_snapped_left;then
 78 				snap_default
 79 			else
 80 				snap_right
 81 			fi
 82 			;;
 83 		M)
 84 			snap_maximize
 85 			;;
 86 		m)
 87 			if $window_maximized;then
 88 				snap_default
 89 			else
 90 				snap_maximize
 91 			fi
 92 			;;
 93 		N)
 94 			snap_minimize
 95 			;;
 96 		n)
 97 			if $window_maximized || $window_snapped_left || $window_snapped_right;then
 98 				snap_default
 99 			else
100 				snap_minimize
101 			fi		
102 			;;
103 		d)
104 			snap_default
105 			;;
106 		i)
107 			print_info
108 			;;
109 		h)
110 			usage
111 			exit
112 			;;
113 		*)
114 			echo "Invalid option: -$OPTARG" >&2
115 			usage
116 			exit 1
117 			;;
118 		esac
119 	done
120 }
121 
122 function check_args
123 {
124 	if [[ "$@" == "" ]];then
125 		usage
126 	fi
127 }
128 
129 function usage
130 {
131 	echo
132 	echo "Usage: gosnap [-L | -l | -R | -r | -M | -m | -N | -n | -d | -i]"
133 	echo "  -L    snap left"
134 	echo "  -l    snap left dynamically"
135 	echo "  -R    snap right"
136 	echo "  -r    snap right dynamically"
137 	echo "  -M    maximize window"
138 	echo "  -m    toggle maximize window"
139 	echo "  -N    minimize window"
140 	echo "  -n    minimize window dynamically"
141 	echo "  -d    resize to default geometry (current default: $window_default)"
142 	echo "  -i    print window information"
143 	echo
144 }
145 
146 function check_command
147 {
148 	if [[ ! -e $(which $1) ]];then
149 		echo -e "\nERROR: $1 was not found on your system!\n"
150 		# If we're on Ubuntu, use the c-n-f functionality
151 		if [[ -x /usr/lib/command-not-found ]];then
152 			python /usr/lib/command-not-found -- $1
153 		elif [[ -x /usr/share/command-not-found ]];then
154 			python /usr/share/command-not-found -- $1
155 		fi
156 		usage
157 		exit 1
158 	fi
159 }
160 
161 function get_window_state
162 {
163 	screen_width=$(xdpyinfo | grep 'dimensions:' | cut -f 2 -d ':' | cut -f 1 -d 'x' | xargs)
164 	window_id=$(xdotool getactivewindow)
165 	window_id_hex=$(printf 0x%x $window_id)
166 	window_properties=$(xprop -id $window_id)
167 	window_state=$(echo $window_properties | grep "_NET_WM_STATE(ATOM)")
168 	window_geometry=$(xdotool getwindowgeometry $window_id)
169 	window_pos_x=$(echo $window_geometry | awk '{print $4}' | awk -F, '{print $1}')
170 	window_pos_y=$(echo $window_geometry | awk '{print $4}' | awk -F, '{print $2}')
171 	window_width=$(echo $window_geometry | awk '{print $8}' | awk -Fx '{print $1}')
172 	window_height=$(echo $window_geometry | awk '{print $8}' | awk -Fx '{print $2}')
173 	window_max_horiz=false
174 	if [ $(echo $window_state | grep -o "_NET_WM_STATE_MAXIMIZED_HORZ") ];then
175 		window_max_horiz=true
176 	fi
177 	window_max_vert=false
178 	if [ $(echo $window_state | grep -o "_NET_WM_STATE_MAXIMIZED_VERT") ];then
179 		window_max_vert=true
180 	fi
181 	window_maximized=false
182 	if $window_max_horiz && $window_max_vert;then
183 		window_maximized=true
184 	fi
185 	window_snapped_left=false
186 	if $window_max_vert && [ $window_pos_x == $left_origin_x ];then
187 		window_snapped_left=true
188 	fi
189 	window_snapped_right=false
190 	if $window_max_vert && [ $window_pos_x == $right_origin_x ];then
191 		window_snapped_right=true
192 	fi
193 }
194 
195 function snap_maximize
196 {
197 	wmctrl -r :ACTIVE: -b add,maximized_vert,maximized_horz
198 }
199 
200 function snap_minimize
201 {
202 	xdotool getactivewindow windowminimize
203 }
204 
205 function snap_default
206 {
207 	wmctrl -r :ACTIVE: -b remove,maximized_vert,maximized_horz
208 	wmctrl -r :ACTIVE: -e $window_default
209 }
210 
211 function snap_left
212 {
213 	if $wmctrl_bug_fix; then snap_default && snap_maximize;fi
214 	halfw=$((($screen_width/2)+$half_width_adjust_left))
215 	wmctrl -r :ACTIVE: -b remove,maximized_vert,maximized_horz &&\
216 	wmctrl -r :ACTIVE: -b add,maximized_vert &&\
217 	wmctrl -r :ACTIVE: -e 0,$snap_left_x,$snap_left_y,$halfw,-1
218 }
219 
220 function snap_right
221 {
222 	if $wmctrl_bug_fix; then snap_default && snap_maximize;fi
223 	halfw=$((($screen_width/2)+$half_width_adjust_right+$snap_right_x))
224 	wmctrl -r :ACTIVE: -b remove,maximized_vert,maximized_horz &&\
225 	wmctrl -r :ACTIVE: -b add,maximized_vert &&\
226 	wmctrl -r :ACTIVE: -e 0,$halfw,$snap_right_y,$halfw,-1
227 }
228 
229 function print_info
230 {
231 	echo "screen_width = $screen_width (half = $(($screen_width/2)))"
232 	echo "Window $window_id ($window_id_hex)"
233 	echo "$(echo $window_geometry | tr -d '\n' | grep -o 'Position.*' | xargs)"
234 	echo "window_max_horiz = $window_max_horiz"
235 	echo "window_max_vert = $window_max_vert"
236 	echo "window_maximized = $window_maximized"
237 	echo "window_snapped_left = $window_snapped_left"
238 	echo "window_snapped_right = $window_snapped_right"
239 	exit
240 }
241 
242 main $@
243 
244 # OTHER NOTES
245 #
246 # xdotool windowmove $(xdotool getactivewindow) 0 0
247 # xdotool windowmove $(xdotool getactivewindow) 100% 100%
248 # xdotool windowsize $(xdotool getactivewindow) 50% 100%
249 # xdotool getwindowgeometry $(xdotool getactivewindow)
250 # 
251 # No window shadow: add this line to /etc/environment then relog
252 #   MUFFIN_NO_SHADOWS=1
253 #