4b2a891
#!/bin/sh
4b2a891
# Start an ephemeral X server.
4b2a891
#
4b2a891
# This is useful for when you want to lauch an X server for a specific
4b2a891
# process. When that process exits, the X server will be killed.
4b2a891
# 
4b2a891
4b2a891
XSERVER=Xvfb
4b2a891
WINMGR=
4b2a891
4b2a891
prog=$0
4b2a891
usage() {
4b2a891
  echo "Usage: $prog [-x XSERVER] [-w WINDOWMANAGER] [-q] [-h] <command>"
4b2a891
  echo "-h   this help"
4b2a891
  echo "-q   quiet"
4b2a891
  echo "-w   window manager process to start once Xserver is up"
4b2a891
  echo "     (default: '$WINMGR')"
4b2a891
  echo "-x   Xserver (and args) to run"
4b2a891
  echo "     (default: '$XSERVER')"
4b2a891
  echo
4b2a891
  echo "This tool will pick an unused DISPLAY value (:0, :1, etc) and"
4b2a891
  echo "start an Xserver on that display, then run your command."
4b2a891
  echo
4b2a891
  echo "Examples:"
4b2a891
  echo "  $prog -x 'Xephyr -screen 1280x720' xterm"
4b2a891
  echo "  $prog -x 'Xvnc -httpd /usr/share/vnc/classes -geometry 1024x768 -depth 24' -w "gnome-session" firefox"
4b2a891
}
4b2a891
4b2a891
quiet() {
4b2a891
  [ "0$QUIET" -eq 1 ]
4b2a891
}
4b2a891
4b2a891
test_x_available() {
4b2a891
  xsocket=$1
4b2a891
  ! test -S $xsocket
4b2a891
}
4b2a891
4b2a891
test_x_healthy() {
4b2a891
  xpid=$1
4b2a891
  xsocket=$2
4b2a891
  displaynum=$3
4b2a891
4b2a891
  # Try xterm to see if X is up.
4b2a891
  if which xterm > /dev/null 2>&1 ; then
4b2a891
    DISPLAY=:$displaynum xterm -e 'true'
4b2a891
    return $?
4b2a891
  fi
4b2a891
4b2a891
  # Try xdotool if available, if xterm is not.
4b2a891
  if which xdotool > /dev/null 2>&1 ; then
4b2a891
    DISPLAY=:$displaynum xdotool getmouselocation > /dev/null 2>&1
4b2a891
    return $?
4b2a891
  fi
4b2a891
4b2a891
  # Try lsof if no X clients (above) are available
4b2a891
  if which lsof > /dev/null 2>&1 ; then
4b2a891
    lsof -p $xpid | grep -qF $xsocket
4b2a891
    return $?
4b2a891
  fi
4b2a891
4b2a891
  echo "Unable to determine if X is healthy (no tools available)"
4b2a891
  return false
4b2a891
}
4b2a891
4b2a891
cleanup() {
4b2a891
  if [ ! -z "$winmgrpid" ] ; then
4b2a891
    kill -TERM "$winmgrpid" || true
4b2a891
  fi
4b2a891
  kill -TERM "$xpid" || true
4b2a891
4b2a891
  pkill -KILL -P $$ || true
4b2a891
}
4b2a891
4b2a891
eval "set -- $( (POSIXLY_CORRECT=1 getopt -s sh +x:w:qh "$@" || echo " "FAIL) | tr -d '\n' )"
4b2a891
4b2a891
while [ "0$#" -gt 0 ] ; do
4b2a891
  case $1 in
4b2a891
    -x) XSERVER="$2"; shift ;;
4b2a891
    -w) WINMGR="$2"; shift ;;
4b2a891
    -q) QUIET=1 ;;
4b2a891
    -h) usage; exit ;;
4b2a891
    --) shift; break ;;
4b2a891
  esac
4b2a891
  shift
4b2a891
done
4b2a891
4b2a891
if [ "$1" = "FAIL" ] ; then
4b2a891
  usage
4b2a891
  exit 1
4b2a891
fi
4b2a891
4b2a891
num=-1
4b2a891
XSERVERNAME=${XSERVER%% *}
4b2a891
if ! which "$XSERVERNAME" > /dev/null 2>&1 ; then
4b2a891
  echo "Unable to find $XSERVERNAME. Aborting."
4b2a891
  cleanup
4b2a891
  exit 1
4b2a891
fi
4b2a891
4b2a891
while true; do 
4b2a891
  num=$(expr $num + 1)
4b2a891
  xsocket=/tmp/.X11-unix/X$num
4b2a891
  quiet || echo "Trying :$num"
4b2a891
  test_x_available $xsocket || continue
4b2a891
  (
4b2a891
    if quiet ; then
4b2a891
      exec > /dev/null
4b2a891
      exec 2> /dev/null
4b2a891
    fi
4b2a891
    echo set -- $XSERVER
4b2a891
    set -- $XSERVER
4b2a891
    cmd=$1
4b2a891
    shift
4b2a891
    exec $cmd :$num "$@"
4b2a891
  ) &
4b2a891
  xpid=$!
4b2a891
4b2a891
  healthy=0
4b2a891
  for i in 1 2 3 4 5 6 7 8 9 ; do
4b2a891
    # Break early if the xserver died
4b2a891
    #ps -p $xpid > /dev/null 2>&1 || break
4b2a891
    kill -0 $xpid > /dev/null 2>&1 || break
4b2a891
4b2a891
    # See if the xserver got a hold of the display socket.
4b2a891
    # If so, the server is up and healthy.
4b2a891
    sleep 1
4b2a891
    if test_x_healthy $xpid $xsocket $num ; then
4b2a891
      quiet || echo "$XSERVERNAME looks healthy. Moving on."
4b2a891
      healthy=1
4b2a891
      break
4b2a891
    fi
4b2a891
    sleep 0.2 || sleep 1 # In case your sleep doesn't take subsecond values
4b2a891
  done
4b2a891
4b2a891
  if [ "0$healthy" -eq 1 ] ; then
4b2a891
    break
4b2a891
  fi
4b2a891
done
4b2a891
4b2a891
export DISPLAY=:$num
4b2a891
quiet || echo "Using display: $DISPLAY"
4b2a891
4b2a891
if [ ! -z "$WINMGR" -a "$WINMGR" != "none" ] ; then
4b2a891
  if ! which $WINMGR > /dev/null 2>&1 ; then
4b2a891
    echo "Cannot find $WINMGR. Aborting."
4b2a891
    cleanup
4b2a891
    exit 1
4b2a891
  fi
4b2a891
  WINMGRNAME=${WINMGR%% *}
4b2a891
  quiet || echo "Starting window manager: $WINMGRNAME"
4b2a891
  (
4b2a891
    if quiet ; then
4b2a891
      exec > /dev/null
4b2a891
      exec 2> /dev/null
4b2a891
    fi
4b2a891
    $WINMGR
4b2a891
  ) &
4b2a891
  winmgrpid=$!
4b2a891
4b2a891
  # Wait for the window manager to startup
4b2a891
  quiet || echo "Waiting for window manager '$WINMGRNAME' to be healthy."
4b2a891
  # Wait for the window manager to start.
4b2a891
  for i in 1 2 3 4 5 6 7 8 9 10 ABORT ; do 
4b2a891
    # A good signal that the WM has started is that the WM_STATE property is
4b2a891
    # set or that any NETWM/ICCCM property is set.
4b2a891
    if xprop -root | egrep -q 'WM_STATE|^_NET' ; then
4b2a891
      quiet || echo "$WINMGRNAME looks healthy. Moving on."
4b2a891
      break;
4b2a891
    fi
4b2a891
    sleep .5
4b2a891
4b2a891
    if [ "$i" = "ABORT" ] ; then
4b2a891
      quiet || echo "Window manager ($WINMGRNAME) seems to have failed starting up."
4b2a891
      cleanup
4b2a891
      exit 1
4b2a891
    fi
4b2a891
  done
4b2a891
fi
4b2a891
4b2a891
quiet || echo "Running: $@"
4b2a891
(
4b2a891
  "$@"
4b2a891
)
4b2a891
exitcode=$?
4b2a891
cleanup
4b2a891
exit $exitcode