Skip to content

Instantly share code, notes, and snippets.

@mlauronen
Last active September 6, 2025 05:49
Show Gist options
  • Select an option

  • Save mlauronen/cb3dc7bf0e5fa4ef7f86aafaf20e73e1 to your computer and use it in GitHub Desktop.

Select an option

Save mlauronen/cb3dc7bf0e5fa4ef7f86aafaf20e73e1 to your computer and use it in GitHub Desktop.
Tile and set X Windows in Linux
#!/bin/bash
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
#
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
# For more information, please refer to <http://unlicense.org/>
# @TODO Wine windows doesn't work correctly
if [ $# -ne 2 ]; then
echo "Usage: $0 <window_id> <command>"
echo "<window_id> can be:"
echo " number Window ID (e.g., 0x1a0000 or 123456)"
echo " '.' Current active window"
echo " '!' Select window"
echo " name Partial window name (e.g., 'firefox')"
echo "Commands:"
echo " -1 lower left corner"
echo " -2 bottom side"
echo " -3 lower right corner"
echo " -4 left side"
echo " -5 full screen"
echo " -6 right side"
echo " -7 upper left corner"
echo " -8 top side"
echo " -9 upper right corner"
echo " -gX,Y,W,H move and resize"
echo " -sW,H resize"
echo " -mX,Y move"
echo " -p print geometry data"
exit 1
fi
PANEL_NAME="xfce4-panel"
MONITOR_WIDTH=1920
MONITOR_HEIGHT=1080
window_id="$1"
command="$2"
# Get Window information
if [ "$window_id" = "!" ]; then
window_id=$(xdotool selectwindow)
elif [ "$window_id" = "." ]; then
window_id=$(xdotool getactivewindow)
if [ -z "$window_id" ]; then
echo "Error: No active window found"
exit 1
fi
elif [[ "$window_id" =~ ^0x[0-9a-fA-F]+$ ]]; then
window_id=$((window_id))
elif [[ ! "$window_id" =~ ^[0-9]+$ ]]; then
window_id=$(wmctrl -l | grep -i "$window_id" | head -n 1 | awk '{print $1}')
if [ -z "$window_id" ]; then
echo "Error: No window found matching partial name: $1"
exit 1
fi
window_id=$((window_id))
fi
if ! xwininfo -id "$window_id" >/dev/null 2>&1; then
echo "Error: Invalid window ID: $window_id"
exit 1
fi
wmctrl -ir "$window_id" -b remove,maximized_vert,maximized_horz
absolute_x=$(xwininfo -id "$window_id" | grep "Absolute upper-left X:" | awk '{print $4}')
absolute_y=$(xwininfo -id "$window_id" | grep "Absolute upper-left Y:" | awk '{print $4}')
if [ "$absolute_x" -lt 0 ]; then
absolute_x=0
fi
if [ "$absolute_y" -lt 0 ]; then
absolute_y=0
fi
relative_x=$(xwininfo -id "$window_id" | grep "Relative upper-left X:" | awk '{print $4}')
relative_y=$(xwininfo -id "$window_id" | grep "Relative upper-left Y:" | awk '{print $4}')
width=$(xwininfo -id "$window_id" | grep "Width:" | awk '{print $2}')
height=$(xwininfo -id "$window_id" | grep "Height:" | awk '{print $2}')
width_fix=$((-(relative_x * 2)))
height_fix=$((-(relative_y + relative_x)))
# Get monitor information
monitor_info=$(xrandr --current)
monitor_x=0
monitor_y=0
while read -r line; do
if [[ $line =~ ([0-9]+)x([0-9]+)\+([0-9]+)\+([0-9]+) ]]; then
mon_w=${BASH_REMATCH[1]}
mon_h=${BASH_REMATCH[2]}
mon_x=${BASH_REMATCH[3]}
mon_y=${BASH_REMATCH[4]}
if (( absolute_x >= mon_x && absolute_x < mon_x + mon_w && absolute_y >= mon_y && absolute_y < mon_y + mon_h )); then
MONITOR_WIDTH=$mon_w
MONITOR_HEIGHT=$mon_h
monitor_x=$mon_x
monitor_y=$mon_y
break
fi
fi
done <<< "$monitor_info"
usable_x=$monitor_x
usable_y=$monitor_y
usable_width=$MONITOR_WIDTH
usable_height=$MONITOR_HEIGHT
# Get Panel information
panel_ids=$(wmctrl -l | grep "$PANEL_NAME" | awk '{print $1}')
if [ -n "$panel_ids" ]; then
while read -r panel_id; do
panel_id=$((panel_id))
panel_info=$(xwininfo -id "$panel_id" 2>/dev/null)
if [ $? -eq 0 ]; then
panel_abs_x=$(echo "$panel_info" | grep "Absolute upper-left X:" | awk '{print $4}')
panel_abs_y=$(echo "$panel_info" | grep "Absolute upper-left Y:" | awk '{print $4}')
panel_width=$(echo "$panel_info" | grep "Width:" | awk '{print $2}')
panel_height=$(echo "$panel_info" | grep "Height:" | awk '{print $2}')
panel_x=$panel_abs_x
panel_y=$panel_abs_y
if [ "$panel_width" -gt 0 ] && [ "$panel_height" -gt 0 ]; then
if (( panel_abs_x >= monitor_x && panel_abs_x < monitor_x + MONITOR_WIDTH &&
panel_abs_y >= monitor_y && panel_abs_y < monitor_y + MONITOR_HEIGHT )); then
if [ "$panel_height" -gt "$panel_width" ]; then
if [ "$panel_x" -eq "$monitor_x" ]; then
usable_x=$((usable_x + panel_width))
usable_width=$((usable_width - panel_width))
elif [ "$panel_x" -eq $((monitor_x + MONITOR_WIDTH - panel_width)) ]; then
usable_width=$((usable_width - panel_width))
fi
else
if [ "$panel_y" -eq "$monitor_y" ]; then
usable_y=$((usable_y + panel_height))
usable_height=$((usable_height - panel_height))
elif [ "$panel_y" -eq $((monitor_y + MONITOR_HEIGHT - panel_height)) ]; then
usable_height=$((usable_height - panel_height))
fi
fi
fi
fi
fi
done <<< "$panel_ids"
fi
half_width=$((usable_width / 2))
half_height=$((usable_height / 2))
# Execute the command
case $command in
-1)
xdotool windowmove "$window_id" $usable_x $((usable_y + half_height))
xdotool windowsize "$window_id" $((half_width + width_fix)) $((half_height + height_fix))
;;
-2)
xdotool windowmove "$window_id" $usable_x $((usable_y + half_height))
xdotool windowsize "$window_id" $((usable_width + width_fix)) $((half_height + height_fix))
;;
-3)
xdotool windowmove "$window_id" $((usable_x + half_width)) $((usable_y + half_height))
xdotool windowsize "$window_id" $((half_width + width_fix)) $((half_height + height_fix))
;;
-4)
xdotool windowmove "$window_id" $usable_x $usable_y
xdotool windowsize "$window_id" $((half_width + width_fix)) $((usable_height + height_fix))
;;
-5)
xdotool windowmove "$window_id" $usable_x $usable_y
xdotool windowsize "$window_id" $((usable_width + width_fix)) $((usable_height + height_fix))
;;
-6)
xdotool windowmove "$window_id" $((usable_x + half_width)) $usable_y
xdotool windowsize "$window_id" $((half_width + width_fix)) $((usable_height + height_fix))
;;
-7)
xdotool windowmove "$window_id" $usable_x $usable_y
xdotool windowsize "$window_id" $((half_width + width_fix)) $((half_height + height_fix))
;;
-8)
xdotool windowmove "$window_id" $usable_x $usable_y
xdotool windowsize "$window_id" $((usable_width + width_fix)) $((half_height + height_fix))
;;
-9)
xdotool windowmove "$window_id" $((usable_x + half_width)) $usable_y
xdotool windowsize "$window_id" $((half_width + width_fix)) $((half_height + height_fix))
;;
-g*)
geom="${command:2}"
IFS=',' read -r g_x g_y g_w g_h <<< "$geom"
if [[ -n $g_x && -n $g_y && -n $g_w && -n $g_h ]]; then
xdotool windowmove "$window_id" "$g_x" "$g_y"
xdotool windowsize "$window_id" "$((g_w + width_fix))" "$((g_h + height_fix))"
else
echo "Error: invalid geometry format. Use -gX,Y,W,H"
exit 1
fi
;;
-s*)
geom="${command:2}"
IFS=',' read -r g_w g_h <<< "$geom"
if [[ -n $g_w && -n $g_h ]]; then
xdotool windowsize "$window_id" "$g_w" "$g_h"
else
echo "Error: invalid geometry format. Use -sW,H"
exit 1
fi
;;
-m*)
geom="${command:2}"
IFS=',' read -r g_x g_y <<< "$geom"
if [[ -n $g_x && -n $g_y ]]; then
xdotool windowmove "$window_id" "$g_x" "$g_y"
else
echo "Error: invalid geometry format. Use -mX,Y"
exit 1
fi
;;
-p)
echo "Window: $window_id"
echo "Monitor: $monitor_x,$monitor_y,$MONITOR_WIDTH,$MONITOR_HEIGHT"
echo "Panel: $panel_y,$panel_height"
echo "Usable: $usable_x,$usable_y,$usable_width,$usable_height"
echo "Absolute: $absolute_x,$absolute_y"
echo "Relative: $relative_x,$relative_y"
echo "Size: $width,$height"
echo "Fix: $width_fix,$height_fix"
;;
*)
echo "Error: Unknown command: $command"
exit 1
;;
esac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment