[
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019 Philipp Schaffrath\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "PREFIX ?= /usr/local\nDESTDIR ?=\nBINDIR ?= $(PREFIX)/bin\nMANDIR ?= $(PREFIX)/share/man\n\nall:\n\t@echo \"Nothing to do, try \\\"make install\\\" instead.\"\n\ninstall-common:\n\t@install -v -d \"$(DESTDIR)$(MANDIR)/man1\" && install -m 0644 -v man/giph.1 \"$(DESTDIR)$(MANDIR)/man1/giph.1\"\n\ninstall: install-common\n\t@install -v -d \"$(DESTDIR)$(BINDIR)/\" && install -m 0755 -v src/giph \"$(DESTDIR)$(BINDIR)/giph\"\n\nuninstall:\n\t@rm -vrf \\\n\t\t\"$(DESTDIR)$(BINDIR)/giph\" \\\n\t\t\"$(DESTDIR)$(MANDIR)/man1/giph.1\"\n\n.PHONY: install uninstall install-common\n"
  },
  {
    "path": "README.md",
    "content": "# giph\ngiph is a screen recorder that records the desktop, a window or selection and encodes it into a gif file. It prints the encoded gif directly to standard output when omitting the output filename.\n\n![demo](https://i.imgur.com/Hoi0fF7.gif)\n\n*I used **giph** to record a gif of **giph** recording a gif*.\n\n## Examples\n\n```bash\n$ giph -s -l -c 1,1,1,0.3 -b 5 -p -5 out.gif \n```\nSelect a window or area with slop. The selection rectangle is highlighted in a transparent blue color abd has a 5px border on the inside. \nAfter stopping the recording with either `ctrl+c`, by running `giph --stop` or by sending a `SIGINT` to the processgroup, the resulting gif is written to `out.gif`.\n\n\n```bash\n$ giph -g 100x200+0+0 -d 5 -t 10\n```\nRecords a 100x200 pixel rectangle in the top left corner of the screen. The recording starts after a 5 seconds countdown and will record for exactly 10 seconds. The resulting gif will be printed to standard output, which makes this able to be piped into other scripts like a file-upload to an image hosting service.\n\n\n```bash\n$ giph -f 30 -t 5 -s -a -m out.webm\n```\nRecords a 5 second video of the users selection at 30 fps. The recording also contains the users desktop audio and microphone. If the recording fails because the default audio source `0` is not the correct one, run `pacmd list-sources` to get the correct source `index` or `name` and pass it to the `-as` parameter instead of using `-a`. Example: `giph -f 30 -t 5 -s -as 1 -m out.webm` (using id) or `giph -f 30 -t 5 -s -as alsa_output.pci-0000_04_00.1.hdmi-stereo.monitor -m out.webm` (using name)\n\n```bash\n$ giph -s -t 10 --format webm | curl -F \"file=@-\" 0x0.st | xclip -selection clipboard\n```\n\nRecords a 10 second webm of the users selection, uploads the video to 0x0.st using curl and copies the returned url to the clipboard.\n\n## Installation\n\n### Arch\n\n```bash\n$ yay -S giph\n```\nOr install [giph-git](https://aur.archlinux.org/packages/giph-git/) to get the latest development version.\n\n### From source\n\nMake sure to install the following dependencies:\n\n - ffmpeg\n - xdotool\n\nOptionally, install the following dependencies:\n\n - slop (`--select`)\n - libnotify (`--notify`)\n - pgrep (`--stop`)\n\nClone the giph repository:\n\n```bash\n$ git clone https://github.com/phisch/giph.git\n```\n\nAnd finally install giph:\n\n```bash\n$ cd giph\n$ sudo make install\n```\n\n## Desktop Makers\n\n<a href=\"https://discord.gg/RqKTeA4uxW\" title=\"Desktop Makers Discord\"><img align=\"left\" width=\"72\" alt=\"type=discord\" src=\"https://user-images.githubusercontent.com/1282767/161089772-d7ad28bf-76eb-4951-b0f0-985afd5ea57a.png\"></a>\n\nI am actively working on giph and other cool projects on the [Desktop Makers Discord](https://discord.gg/RqKTeA4uxW). It aims to be a community for communities of Linux desktop related projects. If you are looking to collaborate with or want to contribute to great projects, this might be the right place for you.\n"
  },
  {
    "path": "man/giph.1",
    "content": ".TH GIPH 1 \"April 2019\" \"MIT License\" \"User Commands\"\n.SH NAME\ngiph \\- record gif from desktop, window or selection\n.SH SYNOPSIS\n.B giph\n.RI [ OPTIONS \"] [\" FILENAME ]\n.br\n.B giph\n[\\fB-g\\fR \\fIGEOMETRY\\fR |\n\\fB-w\\fR \\fIINT\\fR |\n\\fB-s\\fR [\\fB-l\\fR] [\\fB-c\\fR \\fIFLOAT,FLOAT,FLOAT,FLOAT\\fR] [\\fB-p\\fR \\fIFLOAT\\fR] [\\fB-b\\fR \\fIFLOAT\\fR]]\n[\\fB-d\\fR \\fIINT\\fR]\n[\\fB-t\\fR \\fIINT\\fR]\n[\\fB-f\\fR \\fIINT\\fR]\n[\\fB--format\\fR \\fISTRING\\fR]\n[\\fB-a\\fR | \\fB -as \\fISTRING\\fR]\n[\\fB-m\\fR | \\fB -ms \\fISTRING\\fR]\n[\\fIFILENAME\\fR]\n.SH DESCRIPTION\n.B giph\nis a screen recorder that records the desktop, a window or selection and encodes it into a video file.\n.br\nThe recorded video file is encoded in one of the supported formats, gif, webm, mp4 or mkv based on the file extension given in [\\fIFILENAME\\fR].\n.br\nWhen omitting [\\fIFILENAME\\fR], the default format \"gif\" is used (use --format to overwrite), and the resulting video directly printed to standard output.\n.SH EXAMPLES\n.TP\n.BI \"giph -g \" \"300x200+600+200 ~/Videos/$(date +%s).png\"\nRecords a 300x200 pixel (width x height) rectangle, that is shifted 600 pixel to the right and 200 pixel down from the top-left corner. The recording stops when either \n.B ctrl+c\nis pressed, or after calling\n.B giph --stop.\n.br\nThe encoded gif will be saved in your users videos directory and has the current timestamp as a name.\n.SH OPTIONS\n.TP\n.BR \\-h \", \" \\-\\-help\nPrint help and exit.\n.TP\n.BR \\-\\-stop\nFinish any running giph recordings\n.TP\n.BR \\-\\-version\nPrint version and exit.\n.TP\n.BR \\-v* \", \" \\-\\-verbose \", \" \\-\\-quiet\nDefines how verbose giph will be. Omitting [\\fIFILENAME\\fR] will overwrite the verbosity to \\fI-1\\fR (quiet).  Use the following list to determine which option to use for each verbosity level:\n.in +2\n\\(bu\n.IB \"-1 \\fR(quiet)\" \" --quiet \\fR - logs errors\"\n.br\n\\(bu\n.IB \" 0 \\fR(normal) - like quiet, also displays interactive components\"\n.br\n\\(bu\n.IB \" 1 \\fR(verbose)\" \" -v\\fR, \\fB--verbose\" \"\\fR - like normal, also logs basic information about steps\"\n.br\n\\(bu\n.IB \" 2 \\fR(very_verbose) \" -vv \"\\fR - like verbose, also logs detailed information about steps\"\n.br\n\\(bu\n.IB \" 3 \\fR(debug) \" -vvv \"\\fR - like very_verbose, also shows background process output\"\n.TP\n.BR \\-s \", \" \\-\\-select\nUses slop to interactively select the desired region or window to record. This uses the\n.B SLOP OPTIONS\ndescribed below.\n.TP\n.BR \\-g \", \" \\-\\-geometry \" \" \\fIGEOMETRY\nSets the region to record.\n.TP\n.BR \\-w \", \" \\-\\-window \" \" \\fIINT\nSets the window id of the desired window to record.\n.TP\n.BR \\-d \", \" \\-\\-delay \" \" \\fIINT\nSets the time in seconds to wait before the recording starts.\n.TP\n.BR \\-t \", \" \\-\\-timer \" \" \\fITIMEDURATION\nSets a fixed time to record. The format is a timeduration as described in the ffmpeg documentation (https://ffmpeg.org/ffmpeg-utils.html#Time-duration). As an example, '10' would mean 10 seconds, '3:30' means 3 minutes and 30 seconds, '1:02:03' means 1 hour, 2 minutes and 3 seconds, and '5.5' means 5.5 seconds.\n.TP\n.BR \\-f \", \" \\-\\-framerate \" \\fIINT\\fR (default: \\fI20\\fR)\"\nSets the desired framerate of the recorded gif. A higher framerate will result in a larger filesize.\n.TP\n.BR \\-\\-format\nOverwrites the format that should be used for the recording. The available formats are\n.IR gif \", \" webm \", \" mp4 \" and \" mkv \".\"\nWhile\n.IR gif\nis the only format that does not support audio recording.\n.TP\n.BR \\-a \", \" \\-\\-audio\nEnables audio recording for formats that support audio.\n.TP\n.BR \\-as \", \" \\-\\-audio-source \" \\fISTRING\\fR (default: '\\fI0\\fR')\"\nDefine which pulseaudio source should be used for the recording. Run `pacmd list-sources` to get a list of all available sources. You can use either the index or the name of the source for this parameter. When setting this parameter, setting\n.BR \\-a \" or \" \\-\\-audio\ncan be omitted.\n.TP\n.BR \\-m \", \" \\-\\-microphone\nEnables microphone recording for formats that support audio.\n.TP\n.BR \\-ms \", \" \\-\\-microphone-source \" \\fISTRING\\fR (default: '\\fIdefault\\fR')\"\nDefine which pulseaudio source should be used for the recording. Run `pacmd list-sources` to get a list of all available sources. You can use either the index or the name of the source for this parameter. When setting this parameter, setting\n.BR \\-m \" or \" \\-\\-microphone\ncan be omitted.\n.TP\n.BR \\-y \", \" \\-\\-notify\nUses notify-send to send an urgent notification if an error happens, or a normal notification when the final gif was saved successfully.\n.SH SLOP OPTIONS\nWhen\n.BR -s \" or \" --select\nis used, slop will be used to get geometry information. Read\n.B slop(1)\nfor more detailed information about slop options. The following options will be passed to slop if they are set:\n.TP\n.BR \\-b \", \" \\-\\-bordersize \" \\fIFLOAT\\fR (default: \\fI1\\fR)\"\nSet the selection border thickness.\n.TP\n.BR \\-p \", \" \\-\\-padding \" \\fIFLOAT\\fR (default: \\fI0\\fR)\"\nSet the selection padding. This can be negative.\n.TP\n.BR \\-to \", \" \\-\\-tolerance \" \\fIFLOAT\\fR (default: \\fI2\\fR)\"\nDefines how far the mouse can move after clicking while still being considered a click.\n.TP\n.BR \\-c \", \" \\-\\-color \" \\fIFLOAT,FLOAT,FLOAT,FLOAT\\fR (default: \\fI0.5,0.5,0.5,1\\fR)\"\nSet the selection color as RGB or ARGB.\n.TP\n.BR \\-r \", \" \\-\\-shader \" \\fISTRING\"\nSet the shader to be used.\n.TP\n.BR \\-n \", \" \\-\\-nodecorations \" \\fIINT\\fR (default: \\fI0\\fR)\"\n.RI \"Tries to select child windows. \" 0 \" is off, \" 1 \" tries to remove decorations, \" 2 \" aggressively tries to remove decorations.\"\n.TP\n.BR \\-l \", \" \\-\\-highlight\n.RB \"Highlight the selection rectangle. This uses the color provided in \" -c \" or \" --color \".\"\n.TP\n.BR \\-k \", \" \\-\\-nokeyboard\nDisable being able to cancel the selection using the keyboard.\n.TP\n.BR \\-o \", \" \\-\\-noopengl\nDisable graphics acceleration.\n.SH SEE ALSO\n.B slop(1)\n"
  },
  {
    "path": "src/giph",
    "content": "#!/usr/bin/env bash\n\nstty -echoctl # don't print ^C when pressing ctrl+c\n\nreadonly VERSION=1.1.1\n\n# verbosity\nVERBOSITY=0\n\n# options\nSLOP=0\nDELAY=0\nFRAMERATE=20\nAUDIO=0\nAUDIO_SOURCE=0\nMICROPHONE=0\nMICROPHONE_SOURCE=default\nFORMAT_OVERWRITE=\"\"\n\nfunction print_version() {\n  echo \"$VERSION\"\n  exit 0\n}\n\nfunction print_help() {\n  cat << EOF\nSYNOPSIS\n    giph [OPTIONS] [FILENAME]\n\nDESCRIPTION\n  giph is a screen recorder that records the desktop, a window or selection and encodes it into a video file.\n  The recorded video file is encoded in one of the supported formats, gif, webm, mp4 or mkv based on the file extension given in [FILENAME].\n  When omitting [FILENAME], the default format \"gif\" is used (use --format to overwrite), and the resulting video directly printed to standard output.\n\nOPTIONS\n  -h,  --help                       Print help and exit.\n       --version                    Print version and exit.\n  -v*, --verbose, --quiet           Set the verbosity.\n  -s,  --select                     Enable slop selection.\n  -g,  --geometry=STRING            Record rectangle by geometry. (like 100x300+0+0)\n  -w,  --window=INT                 Record window by id.\n  -d,  --delay=INT                  Time in seconds before the recording starts.\n  -t,  --timer=TIMEDURATION         Time of the recording. (e.g. 10 for 10 seconds or 1:30 for 1 minute 30 seconds)\n  -f,  --framerate=INT              Set the framerate.\n       --format                     Set the wanted output format. This overwrites the autodetection.\n  -a,  --audio                      Enable audio recording.\n  -as, --audio-source=STRING        Overwrite the default audio source.\n  -m,  --microphone                 Enable microphone recording.\n  -ms, --microphone-source=STRING   Overwrite the default microphone source.\n  -y,  --notify                     Send notification on error or success.\n       --stop                       Finish any running giph recordings.\n\nSLOP OPTIONS\n  -b, --bordersize=FLOAT                Set the selection border thickness.\n  -p, --padding=FLOAT                   Set the selection padding.\n  -to, --tolerance=FLOAT                Set how far the mouse can move after clicking before recrangle draws.\n  -c, --color=FLOAT,FLOAT,FLOAT,FLOAT   Set the selection color.\n  -r, --shader=STRING                   Set the shader to be used.\n  -n, --nodecorations=INT               Set how aggresively decorations should be avoided.\n  -l, --highlight                       Highlight the selection rectangle.\n  -k, --nokeyboard                      Disable cancel through keypress.\n  -o, --noopengl                        Disable graphics acceleration.\nEOF\n\n  exit 0\n}\n\n# log a message - (message:string, verbosity:int, timestamp:bool, stop_execution:bool, no_trailing_newline:bool)\nfunction log() {\n  [ \"${2:-1}\" -gt \"$VERBOSITY\" ] && return 0\n\n  log=(echo -e)\n  [ \"$5\" = true ] && log+=(-n)\n  [ \"$3\" = true ] && log+=(\"\\033[0;37m$(date '+%Y-%m-%d %H:%M:%S'):\\033[0m\")\n  log+=(\"$1\")\n  \"${log[@]}\"\n\n  [ \"$4\" = true ] && exit 1\n}\n\nfunction log_error() {\n  notify \"$1\" \"critical\"\n  log \"\\033[0;31mERROR:\\033[0m $1\" -1 \"${2:-true}\" true\n}\n\nfunction log_warning() {\n  log \"\\033[0;33mWARNING:\\033[0m $1\" 0 \"${2:-true}\"\n}\n\nfunction log_success() {\n  notify \"$1\" \"normal\"\n  log \"\\033[0;32mSUCCESS:\\033[0m $1\" 0 \"${2:-true}\"\n}\n\nfunction log_info() {\n  log \"\\033[0;36mINFO:\\033[0m $1\" 0 \"${2:-true}\"\n}\n\nfunction notify() {\n  [ \"$NOTIFY\" = 1 ] && {\n    notify=(notify-send -t 3000)\n    notify+=(-u \"$2\")\n    notify+=(\"giph\" \"$1\")\n    \"${notify[@]}\"\n  }\n}\n\nif [ -z \"$1\" ]; then\n  print_help\nfi\n\n# flag handling\nwhile [[ \"$1\" == -* ]]; do\n  case \"$1\" in\n  -h|--help)\n    print_help\n    ;;\n  --version)\n    print_version\n    ;;\n  -v*)\n    (( VERBOSITY += ${#1} - 1 ))\n    ;;\n  --verbose)\n    (( VERBOSITY++ ))\n    ;;\n  --quiet)\n    VERBOSITY=-1\n    ;;\n  --stop)\n    SHOULD_STOP=true\n    ;;\n  -s|--select)\n    SLOP=1\n    ;;\n  -g|--geometry)\n    shift\n    GEOMETRY=\"$1\"\n    ;;\n  -w|--window)\n    shift\n    WINDOW=\"$1\"\n    ;;\n  -d|--delay)\n    shift\n    DELAY=\"$1\"\n    ;;\n  -t|--timer)\n    shift\n    TIMER=\"$1\"\n    ;;\n  -f|--framerate)\n    shift\n    FRAMERATE=\"$1\"\n    ;;\n  --format)\n    shift\n    FORMAT_OVERWRITE=\"$1\"\n    ;;\n  -a|--audio)\n    AUDIO=1\n    ;;\n  -as|--audio-source)\n    shift\n    AUDIO=1\n    AUDIO_SOURCE=\"$1\"\n    ;;\n  -m|--microphone)\n    MICROPHONE=1\n    ;;\n  -ms|--microphone-source)\n    shift\n    MICROPHONE=1\n    MICROPHONE_SOURCE=\"$1\"\n    ;;\n  -y|--notify)\n    NOTIFY=1\n    ;;\n  -b|--bordersize)\n    shift\n    SLOP_BORDERSIZE=\"$1\"\n    ;;\n  -p|--padding)\n    shift\n    SLOP_PADDING=\"$1\"\n    ;;\n  -to|--tolerance)\n    shift\n    SLOP_TOLERANCE=\"$1\"\n    ;;\n  -c|--color)\n    shift\n    SLOP_COLOR=\"$1\"\n    ;;\n  -r|--shader)\n    shift\n    SLOP_SHADER=\"$1\"\n    ;;\n  -n|--nodecorations)\n    shift\n    SLOP_NODECORATIONS=\"$1\"\n    ;;\n  -l|--highlight)\n    SLOP_HIGHLIGHT=true\n    ;;\n  -k|--nokeyboard)\n    SLOP_NOKEYBOARD=true\n    ;;\n  -o|--noopengl)\n    SLOP_NOOPENGL=true\n    ;;\n  -*)\n    log_error \"option '$1' does not exist\" false\n    ;;\n  esac\n  shift\ndone\n\n# set verbosity to -1 if the file should be printed to stdout\n[ -n \"$1\" ] && OUTPUT_FILE=\"$1\" || VERBOSITY=-1\n\ncase \"$OUTPUT_FILE\" in\n    *.webm) FORMAT=\"webm\" ;;\n    *.mp4)  FORMAT=\"mp4\" ;;\n    *.mkv)  FORMAT=\"mkv\" ;;\n    *)      FORMAT=\"gif\" ;;\nesac\n\nif [ -n \"$FORMAT_OVERWRITE\" ]; then\n  case \"$FORMAT_OVERWRITE\" in\n    webm|mp4|mkv|gif) FORMAT=\"$FORMAT_OVERWRITE\";;\n    *) log_error \"'$FORMAT_OVERWRITE' is not a supported format.\"\n  esac\nfi\n\nfunction get_geometry() {\n  if [ \"$SLOP\" = 1 ]; then\n    log \"using slop to determine recording geometry\" 1 true\n    get_geometry_from_slop\n  elif [ -n \"$GEOMETRY\" ]; then\n    log \"using provided geometry string\" 1 true\n    get_geometry_from_string\n  elif [ -n \"$WINDOW\" ]; then\n    log \"determining geometry from provided window id\" 1 true\n    get_geometry_from_window_id \"$WINDOW\"\n  else\n    log_warning \"no method to provide geometry given, using full desktop geometry instead\"\n    log \"determining desktop geometry\" 1 true\n    get_geometry_for_desktop\n  fi\n\n  log \"geometry string: '$GEOMETRY_STRING'\" 2 true\n  parse_geometry_string\n}\n\nfunction get_geometry_from_slop() {\n  slop=(slop -f \"%g\")\n  [ -n \"$SLOP_BORDERSIZE\" ] && slop+=(-b \"$SLOP_BORDERSIZE\")\n  [ -n \"$SLOP_PADDING\" ] && slop+=(-p \"$SLOP_PADDING\")\n  [ -n \"$SLOP_TOLERANCE\" ] && slop+=(-t \"$SLOP_TOLERANCE\")\n  [ -n \"$SLOP_COLOR\" ] && slop+=(-c \"$SLOP_COLOR\")\n  [ -n \"$SLOP_SHADER\" ] && slop+=(-r \"$SLOP_SHADER\")\n  [ -n \"$SLOP_NODECORATIONS\" ] && slop+=(-n \"$SLOP_NODECORATIONS\")\n  [ -n \"$SLOP_HIGHLIGHT\" ] && slop+=(-l)\n  [ -n \"$SLOP_NOKEYBOARD\" ] && slop+=(-k)\n  [ -n \"$SLOP_NOOPENGL\" ] && slop+=(-o)\n  [ \"$VERBOSITY\" -lt \"3\" ] && slop+=(-q)\n\n  slop_command=\"${slop[*]}\"\n  log \"slop command: '$slop_command'\" 2 true\n\n  slop_value=\"$($slop_command)\"\n  [ \"$?\" -eq 1 ] && log_error \"slop selection got canceled\"\n\n  GEOMETRY_STRING=\"$slop_value\"\n}\n\nfunction get_geometry_from_string() {\n  GEOMETRY_STRING=\"$GEOMETRY\"\n}\n\nfunction get_geometry_from_window_id() {\n  xdotool_output=\"$(xdotool getwindowgeometry --shell \"$1\" 2> /dev/null)\"\n  [ \"$?\" = 1 ] && log_error \"window with id $1 does not exist\"\n\n  WIDTH=0\n  HEIGHT=0\n\n  eval \"$xdotool_output\"\n\n  [[ \"$X\" != \"-\"* ]] && X=\"+$X\"\n  [[ \"$Y\" != \"-\"* ]] && Y=\"+$Y\"\n\n  GEOMETRY_STRING=\"${WIDTH}x${HEIGHT}${X}${Y}\"\n}\n\nfunction get_geometry_for_desktop() {\n  root_window_id=\"$(xdotool search --maxdepth 0 --class '')\"\n  get_geometry_from_window_id \"$root_window_id\"\n}\n\nfunction parse_geometry_string() {\n  [[ \"$GEOMETRY_STRING\" =~ ([0-9]+)x([0-9]+)[+]?([-]?[0-9]+)[+]?([-]?[0-9]+) ]]\n\n  width=\"${BASH_REMATCH[1]}\"\n  height=\"${BASH_REMATCH[2]}\"\n  x=\"${BASH_REMATCH[3]}\"\n  y=\"${BASH_REMATCH[4]}\"\n\n  [ -z \"$width\" ] || [ -z \"$height\" ] || [ -z \"$x\" ] || [ -z \"$y\" ] && {\n    log_error \"could not parse geometry string '$GEOMETRY_STRING'\"\n  }\n\n  get_geometry_for_desktop\n\n  [ \"$x\" -gt \"$WIDTH\" ] || [ \"$((x + width))\" -lt 0 ] || [ \"$y\" -gt \"$HEIGHT\" ] || [ \"$((y + height))\" -lt 0  ] && {\n    log_error \"the area to record is fully outside of the root window\"\n  }\n\n  [ \"$x\" -lt 0 ] && {\n    width=\"$((width + x))\"\n    x=\"0\"\n  }\n\n  [ \"$y\" -lt 0 ] && {\n    height=\"$((height + y))\"\n    y=\"0\"\n  }\n\n  [ \"$((x+width))\" -gt \"$WIDTH\" ] && {\n      ((width-=\"$((x + width - WIDTH))\"))\n  }\n\n  [ \"$((y+height))\" -gt \"$HEIGHT\" ] && {\n      ((height-=\"$((y + height - HEIGHT))\"))\n  }\n\n  log \"parsed geometry: width=$width, height=$height, x=$x, y=$y\" 2 true\n}\n\nfunction create_temporary_directory() {\n  TEMP_DIRECTORY=\"$(mktemp -d)\"\n  [ \"$?\" = 1 ] && log_error \"could not create temporary directory\"\n  log \"created temporary directory $TEMP_DIRECTORY\" 2 true\n}\n\nfunction record() {\n  ffmpeg=(ffmpeg -f x11grab -s \"$width\"x\"$height\" -i \"$DISPLAY+$x,$y\")\n\n  if [ \"$FORMAT\" != \"gif\" ]; then\n    [ \"$AUDIO\" = 1 ] && ffmpeg+=(-f pulse -i \"$AUDIO_SOURCE\")\n    [ \"$MICROPHONE\" = 1 ] && ffmpeg+=(-f pulse -i \"$MICROPHONE_SOURCE\")\n    [ \"$AUDIO\" = 1 ] && [ \"$MICROPHONE\" = 1 ] && ffmpeg+=(-filter_complex amerge -ac 2)\n  fi\n\n  ffmpeg+=(-vf \"crop=trunc(iw/2)*2:trunc(ih/2)*2\")\n\n  [ \"$VERBOSITY\" -lt \"3\" ] && ffmpeg+=(-loglevel \"quiet\")\n  [ -n \"$TIMER\" ] && [ \"$TIMER\" -gt 0 ] && ffmpeg+=(-t \"$TIMER\")\n\n  ffmpeg+=(-r \"$FRAMERATE\")\n\n  case \"$FORMAT\" in\n    mp4|mkv)\n      ffmpeg+=(-pix_fmt yuv420p)\n      ffmpeg+=(-crf 15)\n      ffmpeg+=(\"$TEMP_DIRECTORY/final.$FORMAT\")\n      ;;\n    gif|webm|*)\n      ffmpeg+=(-preset veryslow)\n      ffmpeg+=(-crf 0)\n      ffmpeg+=(\"$TEMP_DIRECTORY/lossless.mp4\")\n      ;;\n  esac\n\n  [ -n \"$DELAY\" ] && [ \"$DELAY\" -gt 0 ] && countdown_cli \"$DELAY\" \"recording starts in\"\n\n  ffmpeg_command=\"${ffmpeg[*]}\"\n  log \"ffmpeg command: '$ffmpeg_command'\" 2 true\n\n  \"${ffmpeg[@]}\" &\n  FFMPEG_PID=\"$!\"\n\n  log \"started recording video with ffmpeg\" 1 true\n\n  if [ -n \"$TIMER\" ] && [ \"$TIMER\" -gt 0 ]; then\n    countdown_cli \"$TIMER\" \"recording stops in\"\n  else\n    stop_recording_handler_cli\n  fi\n\n  wait \"$FFMPEG_PID\"\n\n  [ \"$?\" = 1 ] && log_error \"recording video with ffmpeg failed\"\n  log \"completed ffmpeg video recording\" 1 true\n}\n\nfunction countdown_cli() {\n  seconds=\"$1\"\n  while [ \"$seconds\" -ge 0 ]; do\n    log \"\\r\\033[K\\033[0;36m$2:\\033[0m $seconds\" 0 false false true\n    if [ \"$seconds\" -gt 0 ]; then\n      sleep 1\n    else\n      log \"\\r\\033[K\" 0 false false true\n    fi\n    : \"$((seconds--))\"\n  done\n}\n\nfunction stop_recording_handler_cli() {\n  log_info \"stop recording with \\033[1;36mctrl+c\\033[0m or call \\033[1;36mgiph --stop\\033[0m\"\n  trap '' INT\n}\n\nfunction encode() {\n  log \"encoding $FORMAT using ffmpeg\" 1 true\n\n  ffmpeg_encode=(ffmpeg -i \"$TEMP_DIRECTORY/lossless.mp4\")\n\n  case \"$FORMAT\" in\n    \"gif\")\n      ffmpeg_encode+=(-vf \"split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse\")\n      ;;\n    \"webm\")\n      ffmpeg_encode+=(-c:v libvpx-vp9)\n      ;;\n  esac\n\n  [ \"$VERBOSITY\" -lt \"3\" ] && ffmpeg_encode+=(-loglevel \"quiet\")\n\n  ffmpeg_encode+=(\"$TEMP_DIRECTORY/final.$FORMAT\")\n\n  ffmpeg_encode_command=\"${ffmpeg_encode[*]}\"\n  log \"ffmpeg encode command: '$ffmpeg_encode_command'\" 2 true\n\n  \"${ffmpeg_encode[@]}\"\n  [ \"$?\" = 1 ] && log_error \"could not encode $FORMAT from lossless recording\"\n}\n\nfunction deliver() {\n  if [ -n \"$OUTPUT_FILE\" ]; then\n    mv \"$1\" \"$OUTPUT_FILE\" && {\n      log_success \"final $FORMAT saved as \\\"$OUTPUT_FILE\\\"\"\n    }\n  else\n    cat \"$1\"\n  fi\n}\n\nfunction delete_temporary_directory() {\n  rm -r \"$TEMP_DIRECTORY\"\n  [ \"$?\" = 1 ] && log_error \"could not delete temporary directory\"\n  log \"deleted temporary directory $TEMP_DIRECTORY\" 2 true\n}\n\nfunction giph() {\n  get_geometry\n  create_temporary_directory\n  record\n\n  if [[ \"$FORMAT\" == \"gif\" || \"$FORMAT\" == \"webm\" ]]; then\n    encode\n  fi\n\n  deliver \"$TEMP_DIRECTORY/final.$FORMAT\"\n\n  delete_temporary_directory\n  exit 0\n}\n\nfunction stop_all_recordings() {\n  for pid in \"$(pgrep -f \"bash.+giph\")\"; do\n    kill -INT -$pid\n  done\n}\n\nif [ -n \"$SHOULD_STOP\" ]; then\n  stop_all_recordings\nelse\n  giph\nfi\n\nwait\n"
  }
]