#!/bin/bash

# =============================================================================
# Take_a_Shot! (TAS) v1.9 by SFR'2013-2014
# (a bit modified for comaptibility with FatDog64)
# GNU GPL v2 applies
# -----------------------------------------------------------------------------
# Requirements: Bash, Gtkdialog, xmessage, ffmpeg/avconv, xwininfo, getcurpos
# Optional: gifsicle (for .gif optimization)
# =============================================================================

export TEXTDOMAIN=tas
export OUTPUT_CHARSET=UTF-8

DEFAULTIMAGEVIEWER=viewnior
DEFAULTIMAGEEDITOR=defaultpaint

[ "`which realpath 2>/dev/null`" ] || alias realpath='readlink -f'

# -----------------------------------------------------------------------------

PROG_NAME="TAS"
PROG_VERSION="1.9"
MY_NAME="$(realpath "$0")"
CONFIG="${HOME}/.take_a_shot.rc"

# -----------------------------------------------------------------------------

trap 'rm -f /tmp/tas_dir /tmp/tas_file' EXIT

# check gtkdialog
[ "`which gtkdialog4 2>/dev/null`" ] && GTKDIALOG=gtkdialog4 || GTKDIALOG=gtkdialog

func_vercmp_gt () {
  [ "${1}" != "$(echo -e "${1}\n${2}" | sort -t '.' -k1,1n -k2,2n -k3,3n -k4,4n -k5,5n | head -n1)" ]
}

GTKDLG_VER="`$GTKDIALOG -v 2>/dev/null | grep -Eo 'version.*\.[0-9] ' | cut -f2 -d ' '`"
func_vercmp_gt "0.8.3" "${GTKDLG_VER}" && GTKDLG_NEW=0 || GTKDLG_NEW=1	# 1 = gtkdialog >= 0.8.3

# check ffmpeg/avconv
[ ! "`which ffmpeg 2>/dev/null`" ] && [ ! "`which avconv 2>/dev/null`" ] && xmessage -timeout 5 -bg red -fn bold -center -title $(gettext "Error") "$(gettext "Please install 'ffmpeg' or 'avconv' first!
Exiting...")" && exit 1
[ "`which ffmpeg 2>/dev/null`" ] && FFMPEG=ffmpeg || FFMPEG=avconv

# get screen resolution
read MAXX MAXY <<< `xwininfo -root | awk 'NR>=8&&NR<=9 {print $2}'`

# -----------------------------------------------------------------------------

func_beep () {
  [ -f /usr/share/audio/beep_high.wav ] && aplay /usr/share/audio/beep_high.wav 2>/dev/null	
}

# take a "quick" snapshot
case "`echo "${1}" | tr [:upper:] [:lower:]`" in
  -q|--quick|quick)
    CNT=1
    SAVENAME="${HOME}/$(gettext 'Quickshot')"
    EXT=png
    TEMPNAME="${SAVENAME}"
    while [ -e "${TEMPNAME}.${EXT}" ]; do
      TEMPNAME="${SAVENAME}(${CNT})"
      ((CNT++))
    done
    SAVENAME="${TEMPNAME}.${EXT}"
    func_beep &
    ERRLOG=$($FFMPEG -f x11grab -s "${MAXX}"x"${MAXY}" -r 25 -i ${DISPLAY} -vframes 1 -pix_fmt rgb24 "${SAVENAME}" 2>&1)
    [ $? -ne 0 ] && { echo "$ERRLOG"; exit 1; }
    exit 0
  ;;
esac

# -----------------------------------------------------------------------------

if [ "${1}" != "relaunch" ]; then
[ "`xwininfo -root | grep Depth | awk '{print $2}'`" -lt 24 ] && xmessage -timeout 10 -bg orange -fn bold -center -title $(gettext "Warning") "$(gettext "Color depth is lower than 24bits.
TAS may not work 100% properly.
Try to use 'xorgwizard' to change the setting.")"
fi

# -----------------------------------------------------------------------------

func_escape () {
  local TMP="${1}"
  TMP="${TMP//\\/\\\\}"			# escape backslashes
  TMP="${TMP//\"/\\\"}"			# escape "
  TMP="${TMP//\$/\\\$}"			# escape $
  TMP="${TMP//\`/\\\`}"			# escape `
  echo -n "$TMP"
}

save_settings () {
  DIRECTORY_TMP="$(func_escape "${DIRECTORY}")"
  FILENAME_TMP="$(func_escape "${FILENAME}")"

  echo "DIRECTORY=\"$DIRECTORY_TMP\"" > $CONFIG
  echo "FILENAME=\"$FILENAME_TMP\"" >> $CONFIG
  echo "DELAY=\"$DELAY\"" >> $CONFIG
  echo "FRAMES=\"$FRAMES\"" >> $CONFIG
  echo "FPS=\"$FPS\"" >> $CONFIG
  echo "EXTRA=\"$EXTRA\"" >> $CONFIG
  echo "FORMAT=\"$FORMAT\"" >> $CONFIG
  echo "SCALE=\"$SCALE\"" >> $CONFIG
  echo "SCALE_PERCENT=\"$SCALE_PERCENT\"" >> $CONFIG
  echo "DATESTAMP=\"$DATESTAMP\"" >> $CONFIG
  echo "TIMESTAMP=\"$TIMESTAMP\"" >> $CONFIG
  echo "MODE=\"$MODE\"" >> $CONFIG
  echo "MOUSE=\"$MOUSE\"" >> $CONFIG
  echo "GIFWARNING=\"$GIFWARNING\"" >> $CONFIG
}

DIRECTORY="${HOME}"
FILENAME="$(gettext 'Screenshot')"
DELAY=1
FRAMES=1
FPS=1
EXTRA=false
FORMAT=PNG
SCALE=false
SCALE_PERCENT=75
DATESTAMP=false
TIMESTAMP=false
MODE=Fullscreen
MOUSE=false
GIFWARNING=on

if [ -f "${CONFIG}" ]; then
 . "${CONFIG}"
fi

# -----------------------------------------------------------------------------

case "${MODE}" in
  Fullscreen)	MODE_1=true;	MODE_2=false;	MODE_3=false	;;
  Window)		MODE_1=false;	MODE_2=true;	MODE_3=false	;;
  Region)		MODE_1=false;	MODE_2=false;	MODE_3=true		;;
esac

DIRNAME="$(echo "${DIRNAME}" | tr '%' '_')"
FILENAME="$(echo "${FILENAME}" | tr '/%' '_')"
[ "${DIRECTORY}" = "" ] && DIRECTORY="${HOME}"
[ "${FILENAME}" = "" ] && FILENAME="${gettext 'Screenshot'}"

echo -n "${DIRECTORY}" > /tmp/tas_dir
echo -n "${FILENAME}" > /tmp/tas_file

SUBTITLE=$(gettext "(Take a Shot!)")
[ "$SUBTITLE" = "(Take a Shot!)" ] && SUBTITLE=""

# -----------------------------------------------------------------------------

WARNING_TEXT="$(gettext "If output GIF it too big (has large resolution and lots of frames) \
an attempt to view it in some of image viewers (Viewnior, GPicView, etc.) \
may result in seizure of all available RAM and hence complete system lockup!

Viewing it in browser (Firefox, Chromium, etc.) is recommended, \
as it takes significantly less RAM.")"

# -----------------------------------------------------------------------------

ICON_LEGACY="/usr/local/lib/X11/mini-icons/mini-camera.xpm"
ICON_MODERN="/usr/share/pixmaps/puppy/photocamera.svg"
[ -f "$ICON_MODERN" ] && ICON="$ICON_MODERN" || ICON="$ICON_LEGACY"

export GIF_WARN='
<window title="'${PROG_NAME}'-'${PROG_VERSION}'" resizable="false" image-name="'${ICON}'">
  <vbox>
    <pixmap>
      <input file stock="gtk-dialog-warning"></input>
    </pixmap>
    <text use-markup="true"><label>"<span fgcolor='"'red'"' size='"'xx-large'"'><b>'$(gettext "WARNING!!!")'</b></span>"</label></text>
    <frame>
      <text><label>"'${WARNING_TEXT}'"</label></text>
    </frame>
      
     <hbox>
      <checkbox>
       <default>false</default>
        <variable>GIFWARNING</variable>
        <label>'$(gettext "Don't show this warning again")'</label>
      </checkbox>
      <text space-expand="true" space-fill="true" visible="false"><label>" "</label></text>
      <button>
        <label>'$(gettext "Continue")'</label>
        <action>exit:CONTINUE</action>
      </button>
    </hbox>  
  </vbox>

<action signal="hide">exit:abort</action>
</window>'

# -----------------------------------------------------------------------------

MAIN='
<window title="'${PROG_NAME}'-'${PROG_VERSION}'" resizable="false" image-name="'${ICON}'" window-position="1">
  <vbox spacing="1">
    <text use-markup="true"><label>"<span font='"'monospace'"' weight='"'bold'"' size='"'xx-large'"'><b>Take a Shot!</b></span>"</label></text>
'

[ "${SUBTITLE}" ] && MAIN=${MAIN}'
<text use-markup="true"><label>"<span style='"'italic'"' size='"'small'"'><b>'${SUBTITLE}'</b></span>"</label></text>
'

export MAIN=${MAIN}'
    <frame>
      <hbox>
        <text xalign="0"><label>'$(gettext "Directory:")'</label></text>
        <text space-expand="true" space-fill="true" visible="false"><label>" "</label></text>
        <entry fs-action="directory" fs-folder="$HOME">
          <variable>DIRECTORY</variable>
          <input file>/tmp/tas_dir</input>
        </entry>
        <button>
	      <input file stock="gtk-open"></input>
		  <action>fileselect:DIRECTORY</action>
		</button>
      </hbox>
      
      <hseparator></hseparator>
      
      <hbox>
        <text xalign="0"><label>'$(gettext "Filename:")'</label></text>
        <text space-expand="true" space-fill="true" visible="false"><label>" "</label></text>
        <entry>
          <variable>FILENAME</variable>
          <input file>/tmp/tas_file</input>
        </entry>
      </hbox>
      <hbox>
        <checkbox>
          <variable>DATESTAMP</variable>
          <label>'$(gettext "Add datestamp")'</label>
          <default>'${DATESTAMP}'</default>
        </checkbox>
        <checkbox>
          <variable>TIMESTAMP</variable>
          <label>'$(gettext "Add timestamp")'</label>
          <default>'${TIMESTAMP}'</default>
        </checkbox>
      </hbox>

      <hseparator></hseparator>
 
      <hbox>
        <text xalign="0"><label>'$(gettext "Delay (in seconds):")'</label></text>
        <text space-expand="true" space-fill="true" visible="false"><label>" "</label></text>
        <spinbutton range-min="0" range-max="60" range-step="1">
          <variable>DELAY</variable>
          <default>'${DELAY}'</default>
        </spinbutton>
      </hbox>

      <hseparator></hseparator>
      
      <hbox>
        <text xalign="0"><label>'$(gettext "Format:")'</label></text>
        <text space-expand="true" space-fill="true" visible="false"><label>" "</label></text>
        <comboboxtext>
          <variable>FORMAT</variable>
          <default>'${FORMAT}'</default>
          <item>PNG</item>
          <item>JPG</item>
          <item>BMP</item>
          <item>TIF</item>
          <item>GIF</item>
        </comboboxtext>

        <vseparator></vseparator>

        <checkbox>
          <label>'$(gettext "Scale (%):")'</label>
          <variable>SCALE</variable>
          <default>'${SCALE}'</default>
          <action>if true enable:SCALE_PERCENT</action>
          <action>if false disable:SCALE_PERCENT</action>
        </checkbox>
        <text space-expand="true" space-fill="true" visible="false"><label>" "</label></text>
        <spinbutton range-min="25" range-max="150" range-step="5" range-value="'${SCALE_PERCENT}'">
          <variable>SCALE_PERCENT</variable>
          <sensitive>'${SCALE}'</sensitive>
        </spinbutton>
      </hbox>
            
      <hseparator></hseparator>
      
      <hbox homogeneous="true">
        <checkbox>
          <variable>MOUSE</variable>
          <default>'${MOUSE}'</default>
          <label>'$(gettext "Include mouse cursor")'</label>
        </checkbox>
      </hbox>
      
      <hseparator></hseparator>
      
      <hbox homogeneous="false"  tooltip-text="'$(gettext "To select a window make sure it's not partially covered by other windows and then click its titlebar.
To select a region click on top-left corner of desired area and release mouse button, then click again on bottom-right corner.")'">
        <radiobutton>
          <variable>MODE_1</variable>
          <default>'${MODE_1}'</default>
          <label>'$(gettext "Fullscreen")'</label>
        </radiobutton>
        <text space-expand="true" space-fill="true" visible="false"><label>" "</label></text>
        <radiobutton>
          <variable>MODE_2</variable>
          <default>'${MODE_2}'</default>
          <label>'$(gettext "Window")'</label>
        </radiobutton>
        <text space-expand="true" space-fill="true" visible="false"><label>" "</label></text>
        <radiobutton>
          <variable>MODE_3</variable>
          <default>'${MODE_3}'</default>
          <label>'$(gettext "Region")'</label>
        </radiobutton>
      </hbox>
      
      <hseparator></hseparator>
      
      <vbox visible="'${EXTRA}'">
        <hbox>
          <text xalign="0"><label>'$(gettext "Number of frames:")'</label></text>
          <text space-expand="true" space-fill="true" visible="false"><label>" "</label></text>
          <spinbutton range-min="1" range-max="999" range-step="1">
            <variable>FRAMES</variable>
            <default>'${FRAMES}'</default>
            '`[ ${GTKDLG_NEW} -eq 1 ] && echo '
              <action condition="command_is_true( [ ${FRAMES} -eq 1 ] && echo true)">disable:FPS</action>
              <action condition="command_is_true( [ ${FRAMES} -gt 1 ] && echo true)">enable:FPS</action>'
            `'
          </spinbutton>
        </hbox>
      
        <hbox> 
          <text xalign="0"><label>'$(gettext "Frames per second:")'</label></text>
          <text space-expand="true" space-fill="true" visible="false"><label>" "</label></text>
          <spinbutton range-min="1" range-max="10" range-step="1" range-value="'${FPS}'">
            <variable>FPS</variable>
            '`[ ${FRAMES} -eq 1 ] && [ ${GTKDLG_NEW} -eq 1 ] && echo '<sensitive>false</sensitive>'`'
          </spinbutton>
        </hbox>
        <variable>varTOGGLE</variable>
      </vbox>

      <togglebutton>
        <label>'$(gettext "Toggle extra options")'</label>
        <default>'${EXTRA}'</default>
        <variable>EXTRA</variable>
        <action>if true show:varTOGGLE</action>
        <action>if false hide:varTOGGLE</action>
      </togglebutton>
                 
    </frame>
    
    <button height-request="64" image-position="top" has-focus="true">
      <label>'$(gettext "Take a Shot!")'</label>
      <input file>'${ICON}'</input>
      <width>32</width>
      <height>32</height>
      <action>exit:COME_BACK</action>
    </button>
    
  </vbox>

<action signal="delete-event">exit:EXIT</action>
<action signal="hide">exit:abort</action>
</window>
'
# -----------------------------------------------------------------------------

I="$IFS"; IFS='
'
for STATEMENTS in $(${GTKDIALOG} -p MAIN); do
  if [[ "${STATEMENTS}" == "FILENAME="* ]]; then
    FILENAME="`echo "${STATEMENTS}" | cut -f2- -d '"' | rev | cut -f2- -d '"' | rev`"
  elif [[ "${STATEMENTS}" == "DIRECTORY="* ]]; then
    DIRECTORY="`echo "${STATEMENTS}" | cut -f2- -d '"' | rev | cut -f2- -d '"' | rev`"
  else
    eval ${STATEMENTS}
  fi
done
IFS="$I"

# -----------------------------------------------------------------------------

if [ "${MODE_1}" = true ]; then
  MODE=Fullscreen
elif [ "${MODE_2}" = true ]; then
  MODE=Window
else
  MODE=Region
fi

# filename can't have / (naturally) but also % (which confuses ffmpeg)
DIRECTORY="`echo "${DIRECTORY}" | tr '%' '_'`"
FILENAME="`echo "${FILENAME}" | tr '/%"' '_'`"

# ---------------------------------------------------------------------------

save_settings

if [ "${EXIT}" != "COME_BACK" ]; then
  exit 0
else
  if [ "${GIFWARNING}" = "on" ] && [ "${FORMAT}" = "GIF" ] && [ "${FRAMES}" -gt 1 ] && [ "${EXTRA}" = "true" ] ; then
    for STATEMENTS in $(${GTKDIALOG} -c -p GIF_WARN); do
      eval ${STATEMENTS}
    done
    [ "$EXIT" != "CONTINUE" ] && exec "$MY_NAME" relaunch
    [ "$GIFWARNING" = "true" ] && GIFWARNING="off" || GIFWARNING="on"
    save_settings
  fi
fi

# ---------------------------------------------------------------------------

[ "${EXTRA}" = "false" ] && FRAMES=1

EXT="`echo ${FORMAT} | tr [:upper:] [:lower:]`"

[ ! -d "${DIRECTORY}" ] && mkdir -p "${DIRECTORY}"
SAVENAME="${DIRECTORY}/${FILENAME}"

[ "${DATESTAMP}" = "true" ] && SAVENAME="${SAVENAME}_$(date +%Y-%m-%d)"
[ "${TIMESTAMP}" = "true" ] && SAVENAME="${SAVENAME}_$(date +%H.%M.%S)"

# ---------------------------------------------------------------------------

CNT=1; TEMPNAME="${SAVENAME}"
if [ $FRAMES -eq 1 ] || [ "${FORMAT}" = "GIF" ] ; then
  while [ -e "${TEMPNAME}.${EXT}" ]; do
    TEMPNAME="${SAVENAME}(${CNT})"
    ((CNT++))
  done
  SAVENAME="${TEMPNAME}.${EXT}"
else
  while [ -e "${TEMPNAME}" ]; do
    TEMPNAME="${SAVENAME}(${CNT})"
    ((CNT++))
  done
  mkdir -p "$TEMPNAME"
  SAVENAME="${TEMPNAME}/${TEMPNAME##*/}_%03d.${EXT}"
fi

# ---------------------------------------------------------------------------

PIX_FMT='-pix_fmt rgb24'
#if [ "$FFMPEG" = "avconv" ]; then
  [ "$FORMAT" = "JPG" ] || [ "$FORMAT" = "BMP" ] && PIX_FMT=''
#fi

[ "${MOUSE}" = false ] && CURSOR="+nomouse" || CURSOR=''
[ "${SCALE}" = "true" ] && SCALE_CMD="-vf scale=iw/(100/${SCALE_PERCENT}):-1" || SCALE_CMD=''

case $MODE in
  Fullscreen)
    WIDTH=${MAXX}; HEIGHT=${MAXY}; OFFSET=""
  ;;
  Window)
    read XPOS YPOS WIDTH HEIGHT <<< `xwininfo -frame | awk 'NR>=8&&NR<=9 {print $4} NR>=12&&NR<=13 {print $2}'`
    [ ${XPOS} -lt 0 ] && WIDTH=$((${WIDTH}+${XPOS})) && XPOS=0
    [ ${YPOS} -lt 0 ] && HEIGHT=$((${HEIGHT}+${YPOS})) && YPOS=0
    [ $((${WIDTH}+${XPOS})) -ge ${MAXX} ] && WIDTH=$((${MAXX}-${XPOS}))
    [ $((${HEIGHT}+${YPOS})) -ge ${MAXY} ] && HEIGHT=$((${MAXY}-${YPOS}))
    OFFSET="+${XPOS},${YPOS}"
  ;;
  Region)
    xwininfo >/dev/null
    read XPOS YPOS <<< `getcurpos`
    xwininfo >/dev/null
    read WIDTH HEIGHT <<< `getcurpos`
    [ ${WIDTH} -lt ${XPOS} ] && { TEMP=${WIDTH}; WIDTH=${XPOS}; XPOS=${TEMP}; }
    [ ${HEIGHT} -lt ${YPOS} ] && { TEMP=${HEIGHT}; HEIGHT=${YPOS}; YPOS=${TEMP}; }   
    WIDTH=$((${WIDTH}-${XPOS}))
    HEIGHT=$((${HEIGHT}-${YPOS}))
    [ ${WIDTH} -lt 5 ] && WIDTH=5
    [ ${HEIGHT} -lt 5 ] && HEIGHT=5
    OFFSET="+${XPOS},${YPOS}"
  ;;
esac

# ---------------------------------------------------------------------------

[ ${FRAMES} -eq 1 ] && FPS=25	# to avoid a strange, initial delay

if [ "$FFMPEG" = "ffmpeg" ]; then
  # Older versions of ffmpeg need '-loop_output 0' in order to loop anim, but it's been deprecated
  [ "`ffmpeg --help 2>&1 | grep 'loop_output'`" ] && LOOP_CMD='-loop_output 0' || LOOP_CMD='-loop 0'
else
  LOOP_CMD=''
fi

sleep ${DELAY}
func_beep &

# Take a Shot!
$FFMPEG -f x11grab -s "${WIDTH}"x"${HEIGHT}" -r ${FPS} -i ${DISPLAY}${OFFSET}${CURSOR} -vframes ${FRAMES} ${PIX_FMT} ${LOOP_CMD} ${SCALE_CMD} "${SAVENAME}" >/tmp/take_a_shot.log 2>&1

if [ $? -ne 0 ]; then
  rm -f "${SAVENAME}"
  xmessage -buttons "$(gettext "View log"):100","$(gettext "Continue"):101","$(gettext "Exit"):102" -center -title "${PROG_NAME}-${PROG_VERSION}" -fg red -bg white -fn bold "$(gettext 'Screenshot not taken, something went wrong!')"
  case $? in
    100)	xmessage -center -title "${PROG_NAME}-${PROG_VERSION}" -file "/tmp/take_a_shot.log" ;;
    101)	exec "${MY_NAME}" relaunch ;;
    *)	exit ;;
  esac
  exec "${MY_NAME}" relaunch
fi

sleep 0.5

# ---------------------------------------------------------------------------

# Optimize GIF and fix the delay (which is broken in GIFs created by older ffmpeg)
if [ "${FORMAT}" = "GIF" ] && [ ${FRAMES} -gt 1 ] && [ "`which gifsicle 2>/dev/null`" ]; then
  xmessage -center -title "${PROG_NAME}-${PROG_VERSION}" -fg darkgreen -bg white -fn bold "$(gettext 'Please wait, optimizing the GIF file...')" & XPID=$!
  gifsicle -V -d $((100/${FPS})) -i --optimize=3 --conserve-memory < "${SAVENAME}" > "${SAVENAME}_${$}_temp_${$}"
  mv "${SAVENAME}_${$}_temp_${$}" "${SAVENAME}"
  kill -s 0 ${XPID} 2>/dev/null && kill ${XPID}
fi

[ ${FRAMES} -gt 1 ] && [ "${FORMAT}" != "GIF" ] && SAVENAME="${SAVENAME//\%03d/001}"

# ---------------------------------------------------------------------------

while :; do
  if [ ! -e "${SAVENAME}" ]; then
    xmessage -timeout 5 -center -title "${PROG_NAME}-${PROG_VERSION}" -fg red -bg white -fn bold "${SAVENAME} $(gettext "no longer exists!")" && break
  else
    xmessage -buttons "$(gettext "View"):100","$(gettext "Edit"):101","$(gettext "Continue"):102","$(gettext "Exit"):103" -center -title "${PROG_NAME}-${PROG_VERSION}" -fg darkgreen -bg white -fn bold "${SAVENAME}"
    case $? in
      100)	$DEFAULTIMAGEVIEWER "${SAVENAME}" ;;
      101)	if [ "${FORMAT}" = "GIF" ] || [ ${FRAMES} -eq 1 ]; then 
		      $DEFAULTIMAGEEDITOR "${SAVENAME}"
		    else
		      $DEFAULTIMAGEEDITOR "${SAVENAME:0:${#SAVENAME}-7}"*".${EXT}"
		    fi ;;
      102)	break ;;
      *)	exit ;;
    esac
  fi
done

exec "${MY_NAME}" relaunch

# =============================================================================
