Scripting

Window Control With Keyboard In Gnome

How to resize windows in Gnome/Metacity using the keyboard rather than mouse.

This is inspired from the ubuntu compizSnap project that emulates the way that Windows7 snaps windows to the side of the screen when they are dragged there. Being a fan of Xmonad, I wanted to emulate both snapping and resizing using the keyboard. Once set up, hitting the key for 'left' will fix the window on the left side and set it's width to 2/3 of the screen size. The next time will reduce the width to 1/2, then 1/3, then 1/4 then it will fully maximise it. The same applies for the other directions.

1: make a file called window_snap in a folder on your path (I suggest ~/bin). 2: paste the following code into it:

#!/usr/bin/env bash
DEBUG=true

function debug_echo {
  if [ $DEBUG == 'true' ]; then 
    echo $@ 
  fi
}

# FUZZ_FACTOR is an amount to allow for taskbars/scrollbars in window calc
FUZZ_FACTOR_HEIGHT=80
FUZZ_FACTOR_WIDTH=20
WINDOW_BORDER_WIDTH=10
WINDOW_BORDER_HEIGHT=38

TOTAL_WIDTH=`xdpyinfo | grep 'dimensions:' | cut -f 2 -d ':' | cut -f 1 -d 'x'`
TOTAL_HEIGHT=`xdpyinfo | grep 'dimensions:' | cut -f 2 -d ':' | cut -f 2 -d 'x'| cut -f 1 -d ' '`
SCREEN_HEIGHT=$(($TOTAL_HEIGHT-$FUZZ_FACTOR_HEIGHT))
SCREEN_WIDTH=$(($TOTAL_WIDTH-$FUZZ_FACTOR_WIDTH))

THREEQUARTER_WIDTH=$(($TOTAL_WIDTH*3/4))
TWOTHIRD_WIDTH=$(($TOTAL_WIDTH*2/3))
HALF_WIDTH=$(($TOTAL_WIDTH/2))
THIRD_WIDTH=$(($TOTAL_WIDTH/3))
QUARTER_WIDTH=$(($TOTAL_WIDTH/4))

THREEQUARTER_HEIGHT=$(($TOTAL_HEIGHT*3/4))
TWOTHIRD_HEIGHT=$(($TOTAL_HEIGHT*2/3))
HALF_HEIGHT=$(($TOTAL_HEIGHT/2))
THIRD_HEIGHT=$(($TOTAL_HEIGHT/3))
QUARTER_HEIGHT=$(($TOTAL_HEIGHT/4))

WINDOW_WIDTH=`xwininfo -id $(xprop -root | awk '/_NET_ACTIVE_WINDOW\(WINDOW\)/{print $NF}') |grep 'Width:' |cut -f2 -d ':'`
WINDOW_HEIGHT=`xwininfo -id $(xprop -root | awk '/_NET_ACTIVE_WINDOW\(WINDOW\)/{print $NF}') |grep 'Height:' |cut -f2 -d ':'`

debug_echo screen width: $SCREEN_WIDTH
debug_echo screen height: $SCREEN_HEIGHT
debug_echo window width: $WINDOW_WIDTH
debug_echo window height: $WINDOW_HEIGHT
debug_echo requested move: $1


case "$1" in
left)

  THREEQUARTER_WIDTH=$(($THREEQUARTER_WIDTH-$WINDOW_BORDER_WIDTH))
  TWOTHIRD_WIDTH=$(($TWOTHIRD_WIDTH-$WINDOW_BORDER_WIDTH))
  HALF_WIDTH=$(($HALF_WIDTH-$WINDOW_BORDER_WIDTH))
  THIRD_WIDTH=$(($THIRD_WIDTH-$WINDOW_BORDER_WIDTH))
  QUARTER_WIDTH=$(($QUARTER_WIDTH-$WINDOW_BORDER_WIDTH))

  if (( $WINDOW_HEIGHT >= $SCREEN_HEIGHT )); then
    debug_echo "already vertically maxed"
    wmctrl -r :ACTIVE: -b remove,maximized_horz
    if (( $WINDOW_WIDTH > $THREEQUARTER_WIDTH )); then
      debug_echo "setting to 3/4"
      wmctrl -r :ACTIVE: -e 0,0,0,$THREEQUARTER_WIDTH,-1
    elif (( $WINDOW_WIDTH > $TWOTHIRD_WIDTH )); then
      debug_echo "setting to 2/3"
      wmctrl -r :ACTIVE: -e 0,0,0,$TWOTHIRD_WIDTH,-1
    elif (( $WINDOW_WIDTH > $HALF_WIDTH)); then
      debug_echo "setting to 1/2"
      wmctrl -r :ACTIVE:  -e 0,0,0,$HALF_WIDTH,-1
    elif (( $WINDOW_WIDTH > $THIRD_WIDTH )); then
      debug_echo "setting to 1/3"
      wmctrl -r :ACTIVE: -e 0,0,0,$THIRD_WIDTH,-1
    elif (( $WINDOW_WIDTH > $QUARTER_WIDTH )); then
      debug_echo "setting to 1/4"
      wmctrl -r :ACTIVE: -e 0,0,0,$QUARTER_WIDTH,-1
    else
      $0 max
    fi
  else
    debug_echo "vertical maxing and setting to 3/4"
    wmctrl -r :ACTIVE: -b remove,maximized_vert,maximized_horz && wmctrl -r :ACTIVE: -b add,maximized_vert
    wmctrl -r :ACTIVE: -e 0,0,0,$THREEQUARTER_WIDTH,-1
  fi 

;;

up)

  THREEQUARTER_HEIGHT=$(($THREEQUARTER_HEIGHT-$WINDOW_BORDER_HEIGHT))
  TWOTHIRD_HEIGHT=$(($TWOTHIRD_HEIGHT-$WINDOW_BORDER_HEIGHT))
  HALF_HEIGHT=$(($HALF_HEIGHT-$WINDOW_BORDER_HEIGHT))
  THIRD_HEIGHT=$(($THIRD_HEIGHT-$WINDOW_BORDER_HEIGHT))
  QUARTER_HEIGHT=$(($QUARTER_HEIGHT-$WINDOW_BORDER_HEIGHT))

  if (( $WINDOW_WIDTH >= $SCREEN_WIDTH )); then
    debug_echo "already horiz maxed"
    wmctrl -r :ACTIVE: -b remove,maximized_vert
    if (( $WINDOW_HEIGHT > $THREEQUARTER_HEIGHT )); then
      debug_echo "setting to 3/4"
      wmctrl -r :ACTIVE: -e 0,0,0,-1,$THREEQUARTER_HEIGHT
    elif (( $WINDOW_HEIGHT > $TWOTHIRD_HEIGHT )); then
      debug_echo "setting to 2/3"
      wmctrl -r :ACTIVE: -e 0,0,0,-1,$TWOTHIRD_HEIGHT
    elif (( $WINDOW_HEIGHT > $HALF_HEIGHT)); then
      debug_echo "setting to 1/2"
      wmctrl -r :ACTIVE:  -e 0,0,0,-1,$HALF_HEIGHT
    elif (( $WINDOW_HEIGHT > $THIRD_HEIGHT )); then
      debug_echo "setting to 1/3"
      wmctrl -r :ACTIVE: -e 0,0,0,-1,$THIRD_HEIGHT
    elif (( $WINDOW_HEIGHT > $QUARTER_HEIGHT )); then
      debug_echo "setting to 1/4"
      wmctrl -r :ACTIVE: -e 0,0,0,-1,$QUARTER_HEIGHT
    else
      $0 max
    fi
  else
    debug_echo "horiz maxing and setting to 3/4"
    wmctrl -r :ACTIVE: -b remove,maximized_vert,maximized_horz && wmctrl -r :ACTIVE: -b add,maximized_horz
    wmctrl -r :ACTIVE: -e 0,0,0,-1,$THREEQUARTER_HEIGHT
  fi 

;;

right)

  THREEQUARTER_LOC=$(($THREEQUARTER_WIDTH+$WINDOW_BORDER_WIDTH))
  TWOTHIRD_LOC=$(($TWOTHIRD_WIDTH+$WINDOW_BORDER_WIDTH))
  HALF_LOC=$(($HALF_WIDTH+$WINDOW_BORDER_WIDTH))
  THIRD_LOC=$(($THIRD_WIDTH+$WINDOW_BORDER_WIDTH))
  QUARTER_LOC=$(($QUARTER_WIDTH+$WINDOW_BORDER_WIDTH))

  THREEQUARTER_WIDTH=$(($THREEQUARTER_WIDTH-$WINDOW_BORDER_WIDTH))
  TWOTHIRD_WIDTH=$(($TWOTHIRD_WIDTH-$WINDOW_BORDER_WIDTH))
  HALF_WIDTH=$(($HALF_WIDTH-$WINDOW_BORDER_WIDTH))
  THIRD_WIDTH=$(($THIRD_WIDTH-$WINDOW_BORDER_WIDTH))
  QUARTER_WIDTH=$(($QUARTER_WIDTH-$WINDOW_BORDER_WIDTH))

  if (( $WINDOW_HEIGHT >= $SCREEN_HEIGHT )); then
    debug_echo "already vertically maxed"
    wmctrl -r :ACTIVE: -b remove,maximized_horz
    if (( $WINDOW_WIDTH > $THREEQUARTER_WIDTH )); then
      debug_echo "setting to 3/4"
      wmctrl -r :ACTIVE: -e 0,$QUARTER_LOC,0,$THREEQUARTER_WIDTH,-1
    elif (( $WINDOW_WIDTH > $TWOTHIRD_WIDTH )); then
      debug_echo "setting to 2/3"
      wmctrl -r :ACTIVE: -e 0,$THIRD_LOC,0,$TWOTHIRD_WIDTH,-1
    elif (( $WINDOW_WIDTH > $HALF_WIDTH)); then
      debug_echo "setting to 1/2"
      wmctrl -r :ACTIVE:  -e 0,$HALF_LOC,0,$HALF_WIDTH,-1
    elif (( $WINDOW_WIDTH > $THIRD_WIDTH )); then
      debug_echo "setting to 1/3"
      wmctrl -r :ACTIVE: -e 0,$TWOTHIRD_LOC,0,$THIRD_WIDTH,-1
    elif (( $WINDOW_WIDTH > $QUARTER_WIDTH )); then
      debug_echo "setting to 1/4"
      wmctrl -r :ACTIVE: -e 0,$THREEQUARTER_LOC,0,$QUARTER_WIDTH,-1
    else
      $0 max
    fi
  else
    debug_echo "vertically maxing and setting to 3/4"
    wmctrl -r :ACTIVE: -b remove,maximized_vert,maximized_horz && wmctrl -r :ACTIVE: -b add,maximized_vert
    wmctrl -r :ACTIVE: -e 0,$QUARTER_LOC,0,$THREEQUARTER_WIDTH,-1
  fi 

;;

down)

  THREEQUARTER_LOC=$(($THREEQUARTER_HEIGHT+$WINDOW_BORDER_HEIGHT))
  TWOTHIRD_LOC=$(($TWOTHIRD_HEIGHT+$WINDOW_BORDER_HEIGHT))
  HALF_LOC=$(($HALF_HEIGHT+$WINDOW_BORDER_HEIGHT))
  THIRD_LOC=$(($THIRD_HEIGHT+$WINDOW_BORDER_HEIGHT))
  QUARTER_LOC=$(($QUARTER_HEIGHT+$WINDOW_BORDER_HEIGHT))

  THREEQUARTER_HEIGHT=$(($THREEQUARTER_HEIGHT-$WINDOW_BORDER_HEIGHT))
  TWOTHIRD_HEIGHT=$(($TWOTHIRD_HEIGHT-$WINDOW_BORDER_HEIGHT))
  HALF_HEIGHT=$(($HALF_HEIGHT-$WINDOW_BORDER_HEIGHT))
  THIRD_HEIGHT=$(($THIRD_HEIGHT-$WINDOW_BORDER_HEIGHT))
  QUARTER_HEIGHT=$(($QUARTER_HEIGHT-$WINDOW_BORDER_HEIGHT))

  if (( $WINDOW_WIDTH >= $SCREEN_WIDTH )); then
    debug_echo "already horiz maxed"
    wmctrl -r :ACTIVE: -b remove,maximized_vert
    if (( $WINDOW_HEIGHT > $THREEQUARTER_HEIGHT )); then
      debug_echo "setting to 3/4"
      wmctrl -r :ACTIVE: -e 0,0,$QUARTER_LOC,-1,$THREEQUARTER_HEIGHT
    elif (( $WINDOW_HEIGHT > $TWOTHIRD_HEIGHT )); then
      debug_echo "setting to 2/3"
      wmctrl -r :ACTIVE: -e 0,0,$THIRD_LOC,-1,$TWOTHIRD_HEIGHT
    elif (( $WINDOW_HEIGHT > $HALF_HEIGHT)); then
      debug_echo "setting to 1/2"
      wmctrl -r :ACTIVE:  -e 0,0,$HALF_LOC,-1,$HALF_HEIGHT
    elif (( $WINDOW_HEIGHT > $THIRD_HEIGHT )); then
      debug_echo "setting to 1/3"
      wmctrl -r :ACTIVE: -e 0,0,$TWOTHIRD_LOC,-1,$THIRD_HEIGHT
    elif (( $WINDOW_HEIGHT > $QUARTER_HEIGHT )); then
      debug_echo "setting to 1/4"
      wmctrl -r :ACTIVE: -e 0,0,$THREEQUARTER_LOC,-1,$QUARTER_HEIGHT
    else
      $0 max
    fi
  else
    debug_echo "horiz maxing and setting to 3/4"
    wmctrl -r :ACTIVE: -b remove,maximized_vert,maximized_horz && wmctrl -r :ACTIVE: -b add,maximized_horz
    wmctrl -r :ACTIVE: -e 0,0,$QUARTER_LOC,-1,$THREEQUARTER_HEIGHT
  fi 

;;

float)

  debug_echo "floating"
  wmctrl -r :ACTIVE: -b remove,maximized_vert,maximized_horz

;;

max)

  debug_echo "maxing"
  wmctrl -r :ACTIVE: -b add,maximized_vert,maximized_horz

;;

*)
	echo "usage: $0 <left, right, up, down, float, max>"
        exit 1
;;
esac

exit 0

3: make it executable

chmod +x ~/bin/window_snap

4: set keys to run the commands. You can do this in gconf-editor or just run the following commands, one at a time:

gconftool-2 --type string --set /apps/metacity/global_keybindings/run_command_9 "<Shift><Super>Left"
gconftool-2 --type string --set /apps/metacity/global_keybindings/run_command_10 "<Shift><Super>Up"
gconftool-2 --type string --set /apps/metacity/global_keybindings/run_command_11 "<Shift><Super>Down"
gconftool-2 --type string --set /apps/metacity/global_keybindings/run_command_12 "<Shift><Super>Right"

gconftool-2 --type string --set /apps/metacity/keybinding_commands/command_9 "window_snap left"
gconftool-2 --type string --set /apps/metacity/keybinding_commands/command_10 "window_snap up"
gconftool-2 --type string --set /apps/metacity/keybinding_commands/command_11 "window_snap down"
gconftool-2 --type string --set /apps/metacity/keybinding_commands/command_12 "window_snap right"

Obviously alter the keys if you want to use different ones.