Thursday, February 19, 2015

Screenshots for the Lazy Linux User

One of tools I used most frequently on Windows isthe Snipping Tool. This relatively tiny program allows you to grab a screenshot and crudely annotate it. There are fancier versions out there, but the Snipping Tool has been built into Windows long enough that you can just depend on it.

So what program should I use to take screenshots on Linux? One solution I found and immediately liked was the peculiarly named scrot. scrot is your typical command line app, so it doesn't do a whole lot (which is ideal!). You can type:

 scrot

and it just grabs a screenshot of the current screen. Or you can dress it up a little bit by adding a delay (which lets you get the right window position) or use -s flag to select a region to snapshot from your current screen.

The "problem" with scrot is that once I'm done with an image, I like to typically annotate it. And for that, I typically use Gimp. So while using scrot by itself is handy, it's not particularly efficient.

Changing gears a bit, it occurred to me that I could just grab a screenshot from within Gimp. That works well enough, but I was curious if I could streamline the process. As is, I found myself navigating to the File » Create menu, clicking on the 'Grab' button, and then switching the foreground color to red so that I could annotate the image in a clear way. Rather than perform these steps manually, I thought I combine them into a bit of script-fu:

(define (bs-snip-it)
  (let ((image (car (plug-in-screenshot RUN-INTERACTIVE 0 0 0 0 0 0)))) ; [XXX]
    (gimp-context-set-foreground '(255 0 0))
    image))

(script-fu-register
 "bs-snip-it"
 "Snip-It"
 "Grab a screenshot and be ready to edit it"
 "Ben Simon"
 "Copyright 2015, Ideas2Executables, LLC"
 "2015/02/18"
 "")

(script-fu-menu-register "bs-snip-it" "<Image>/File/Create")

I have to admit though, I still wasn't satisfied. To grab a screenshot I needed to switch to gimp, kick off this plugin, and interact with the screenshot dialog. I thought I could improve this by running plug-in-screenshot with RUN-NONINTERACTIVE. However, there appears to be a bug in the C code that keeps this from properly working. And regardless, having to switch to Gimp in the first place is a pain.

So on to version 3.0 I went. I ended up combing both scrot and Gimp, driving the whole thing with a shell script:

#!/bin/bash
##
## grab a screenshot and open it in gimp
##
sleep .2
img=$HOME/Pictures/screenshots/`date +%Y-%m-%d-%H%M`.png

(echo "Select area to screen shot" | festival --tts) &
scrot -s $img

startgimp $img
sendgimp "(gimp-context-set-foreground '(255 0 0))"

I wired this script into ratpoison with the following line in my .ratpoisonrc:

  definekey top s-g exec snipit

Now, when I hit Windows-g (Windows is the Super key, hence the s- prefix) the above script is executed. The sleep .2 is required by ratposion, without it the script doesn't execute properly.

The first order of business of the script is to read aloud some instructions to me. Then it kicks off scrot -s $img which allows me to select just the area of interest and saves it into an image.

From there, startgimp is executed, which as you'll see below is a thin shell script wrapper around Gimp. Gimp already has the nice feature that if you hand it an image and there's an instance running, it won't start a new instance, but will use the existing one. The next line sends a bit of code to gimp telling to set the foreground color to red.

This means that I hit one keystroke, select the area of interest, and Gimp pops up in front of me, ready to annotate away. So for now, I'm happy with my screenshot capability.

The scripts below, startgimp and sendgimp leverage the Script-Fu server built into Gimp. When this server is running it's possible to send Gimp arbitrary commands, essentially scripting it from the command line. startgimp kicks off Gimp, insuring the Script-Fu server is running, and sendgimp sends script-fu code to Gimp using the correct protocol. These scripts should be reusable outside of the screenshot context. Essentially, I now have command line scripting of Gimp, to go along with its built in plugin based architecture.

Here are those scripts:

# ########################################################################
# startgimp
# ########################################################################
#!/bin/bash

##
## Kick start gimp off just the right way
##
SERVER_LOG=$HOME/.gimp-2.8/server.log

exec gimp -b "(plug-in-script-fu-server 1 \"127.0.0.1\" 10008 \"$SERVER_LOG\")" "$@"


# ########################################################################
# sendgimp
# ########################################################################
#!/bin/bash

##
## Send gimp all the expressions on the command line. Or, if
## no command line, then read from stdin
##

SERVER_HOST=127.0.0.1
SERVER_PORT=10008

function gimp_send {
  expr="$1"
  len=`echo -n $expr | wc -c | cut -d ' ' -f1`
  (perl -e "print pack('an', 'G', $len)" ;
   echo $expr) | nc $SERVER_HOST $SERVER_PORT
}

if [ -z "$1" ] ; then
  expr=`cat`
  gimp_send "$expr"
else
  while [ -n "$1" ]; do
    gimp_send "$1" 
    shift
  done
fi

No comments:

Post a Comment