| < Day Day Up > |
Strong-Arming the System—Brute Force Behavior Modification
Sometimes, there just isn't a configuration option available to let you make something work the way you want it to. The GUI tools don't have a button for you to click, the configuration files for the software don't list an option for you, and the
Defaults
database contains no useful parameters. If you're willing to apply what you've learned so far in this book, there still might be ways for you to make your system do what you want. The key is remembering that underneath it all, Mac OS X is running Unix, and the Unix
The Sneaky Way—Inserting ImpostersDepending on exactly what you're trying to change, there are two primary ways to go about this. The less obnoxious way is to interpose some software of your own devising between what the system is trying to do and what it's actually doing. Because most everything is a small, special-purpose program, you can often insert an imposter program that looks and talks to the system like the program it thinks it's calling. The imposter can then call the actual program (or not, if you don't need to) with any modifications to inputs that you want, unrestricted by what the system allows you to conveniently configure.
Let's take the Command-Shift-3/Command-Shift-4 screen capture facility that's built into the operating system as an example. Pressing Command-Shift-3 takes a screenshot of what currently appears on the screen. Command-Shift-4 lets you select a region of the screen or a particular window to save an image of instead. Both of these functions
If you really want that functionality, you're willing to strong-arm the system into giving it to you even though it doesn't appear to be an option, and you accept the consequences of the changes you'll be making, there is a way to accomplish your goal. The solution requires replacing bits of the software underlying the user interface with things that do what you want, instead of what Apple made them do. The consequence is that your system will no longer be quite as Apple delivered it, and there's no telling what an Apple software update will do when it encounters these modified files.
The key to solving the problem is to recognize that when you press Command-Shift-3 or Command-Shift-4, the GUI invokes a command-line application,
/usr/sbin/screencapture
. The
Table 16.12. Command Documentation for screencapture
The fact that the screen image is captured by a command-line application should immediately bring to mind a possible way that a solution might be approached. You can write small command-line programs, right? You learned how to do this in Chapter 15, when you learned about shell scripts. A shell script looks for all the world just like any other program, but you can fill it with the automated execution of any command-line commands that you want. So, what would happen when you press Command-Shift-3, if you were to find the screencapture program as delivered by Apple, rename it so that the system couldn't find it, and then replace it with a shell script of your own devising? Presuming that you write a syntactically correct shell script, no more and no less than exactly what you put in your shell script. Let's see what happens: You'll find the screencapture program in /usr/sbin/ . As root , move it to /usr/sbin/screencapture-o . brezup:ray ray $ su Password: brezup:root ray # cd /usr/sbin brezup:root sbin # mv screencapture screencapture-o Now replace it with a small shell script so that you can see what's being passed to the screencapture program when Command-Shift-3 and Command-Shift-4 are pressed. brezup:root sbin # cat > screencapture #!/bin/csh echo "option 0brezup:root sbin # cat > screencapture #!/bin/csh echo "option 0 $0" > /tmp/screencapopts echo "option 1 $1" >> /tmp/screencapopts echo "option 2 $2" >> /tmp/screencapopts echo "option 3 $3" >> /tmp/screencapopts" > /tmp/screencapopts echo "option 1 " >> /tmp/screencapopts echo "option 2 " >> /tmp/screencapopts echo "option 3 " >> /tmp/screencapopts Press Control-d to end the cat session; then make the new screencapture script executable. brezup:root sbin # chmod 755 screencapture Press Command-Shift-3 and see what happens—depending on whether you're Tiger, or a previous version of Mac OS X, you'll see two slightly different behaviors: On Tiger: <Command-Shift-3> brezup:root sbin # cat /tmp/screencapopts option 0 /usr/sbin/screencapture option 1 -f option 2 -tpng option 3 /Volumes/Wills_Data/ray/Desktop/Picture 1.pdf Previous versions: <Command-Shift-3> brezup:root sbin # cat /tmp/screencapopts option 0 /usr/sbin/screencapture option 1 -f option 2 /Volumes/Wills_Data/ray/Desktop/Picture 1.pdf option 3
Also check Command-Shift-4 and both variants with Control held down as well (the Control variants are supposed to place the capture on the clipboard): <Command-Shift-4> brezup:root sbin # cat /tmp/screencapopts option 0 /usr/sbin/screencapture option 1 -i option 2 -tpng option 3 /Volumes/Wills_Data/ray/Desktop/Picture 2.pdf <Command-Control-Shift-3> brezup:root sbin # cat /tmp/screencapopts option 0 /usr/sbin/screencapture option 1 -c option 2 -tpng option 3 <Command-Control-Shift-4> brezup:root sbin # cat /tmp/screencapopts option 0 /usr/sbin/screencapture option 1 -ic option 2 -tpng option 3 (Panther, and earlier-version users will see similar output, lacking the -tpng parameter.) From these, it's clear that the options are always passed as the first parameter to the command (which is apparently what that do-nothing -f option is for—filling space as parameter 1 when no real parameter is required), and the filename, if there is one, is always parameter 3 on Tiger, and parameter 2 in earlier versions. This is lucky for us. We don't need to do any fancy option parsing. So long as we can figure out how to either pass the parameters we want, instead of the hard-coded PNG format, or to convert the output of Apple's screencapture (now screencapture-o ) into a friendlier file format, we can just pass options and parameters straight from our script to it, and all should be well. If we are working with Tiger, our immediate task is now simple: how to change the -tpng parameter to something we prefer. This requires nothing more than rewriting our new screencapture script so that it calls Apple's screencapture (now screencapture-o ), and passes a -t< format > option with our preferred format instead of -tpng . To summarize, the following things must be done to make a completely functional shell script wrapper for screencapture-o , which will force the output into whatever file format we prefer:
A script, stored in /usr/sbin/screencapture , such as this would do the trick: #!/bin/csh set options=""; set type="-ttif"; set filename=""; /usr/sbin/screencapture-o $options $type "$filename"; exit The only problem with this, is that parameter 3, the filename, is being passed in by some external process, and it's still being sent in the form of Picture #.png , rather than Picture #.< ourformat > , as we'd prefer. There are a number of ways to work around this problem. Because we're already overwriting the type information, the method that comes to mind first might be to also overwrite the supplied filename with one of our own choosing. Using what you know about shell scripting and rewriting file suffixes, you might construct a script such as this: #!/bin/csh set options=""; set type="tif"; set typeoption = "-t$type"; set filebase=":r"; set filename="$filebase.$type"; /usr/sbin/screencapture-o $options $typeoption "$filename"; exit
This comes
so close
to working
All is not lost, however. There are few things a computer can do to keep a determined user from
#!/bin/csh set options=""; set toldtype=""; if ( $%3 > 0 ) then set type="tif"; set typeoption = "-t$type"; set toldfile = "" set datestr=`date "+%y%m%d-%H:%M:%S"` set wrkdir = "$toldfile:h" set outfile="$wrkdir/Picture $datestr.type" \rm -f "$toldfile" /usr/sbin/screencapture-o $options $typeoption "$outfile" exit endif /usr/sbin/screencapture-o $options $toldtype exit
This might be beginning to look a little bit complex, and it contains a couple things that we haven't discussed in the text, but it's actually pretty easy to understand when broken down into
To most quickly begin to understand what the script does, take the case where the
if
statement fails—that is, when the third option,
$3
, contains no text (there is no filename). In this case, execution
If there is a filename in
$3
, we need to process it as
It's a little difficult to
If you're working in Panther or some other earlier version of Mac OS X, you don't have the option of directly controlling the file type that Apple's
screencapture
writes. Instead, you would need to find a way to convert the fixed format output from Apple's
screencapture
into whatever format you preferred. Because Tiger gives you the ability to control the format, that isn't necessary here, but the general technique is
I'm partial to storing my images as TIFFs, so I'm going to use the tiff24nc output format, which is uncompressed 24-bit TIFF. Fiddling around at the command line, I find that the syntax shown in the following line, converts a .pdf file into a TIFF format file for me. /usr/local/bin/gs -q -dBATCH -dNOPAUSE -sDEVICE=tiff24nc -sOutputFile=< tiffile > < pdffile > For example, brezup:root Desktop # / usr/local/bin/gs -q -dBATCH -dNOPAUSE creates the file Picture 1.tif in my current directory, and it is a properly formatted TIFF file (there's a reason I've used .tif instead of .tiff , which will be explained shortly).
{% if main.adsdop %}{% include 'adsenceinline.tpl' %}{% endif %} Even with a screencapture that insists on writing PDFs (and doesn't accept a -t parameter as option 2), the TIFF file final format version of our little hack can be accomplished with a relatively simple script, like this: #!/bin/csh set options=""; if ( $%2 > 0 ) then set pdffile = "" set datestr=`date "+%y%m%d-%H:%M:%S"` set wrkdir = "$pdffile:h" set tmpfile="$wrkdir/.Picture $datestr.tmp" set tiffile="$wrkdir/Picture $datestr.tif" \rm -f "$pdffile" /usr/sbin/screencapture-o $options "$tmpfile" gs -q -dBATCH -dNOPAUSE -sDEVICE=tiff24nc -sOutputFile="$tiffile" "$tmpfile" \rm -f "$tmpfile" exit endif /usr/sbin/screencapture-o $options exit
Note that there's very little change from the version that uses
screencapture
's new
-t
option. This version needs only to save the output from
screencapture
into a temporary file (named much like the final file, only with a
.
The Brutal Way—Organ Transplants
Sometimes, inserting imposters isn't a clean solution. Other times, it just can't give you all the functionality you really want. In the
screencapture
example given previously, the most annoying issue remaining is that the filename is a bit
Remember back in the early chapters covering Unix when we said that Unix doesn't really know or care what's in a file; that if you tried to execute a datafile, Unix would let you; and that likewise you could read applications like they were giant text files with text editors? Well, emacs is your application-modifying friend. If you dig around the /System/Library/CoreServices/ directory, you'll eventually find that SystemUIServer is the part of the system that's calling screencapture when you press the Command-Shift-3/4 key combinations. I found it by using grep from /System/Library/CoreServices/ , as in grep screencapture /System/Library/CoreServices/*/*/*/* 2>&1 grep "matches" .
If you run strings on the file that matches, the following interesting tidbits show up:
brezup:root sbin #
strings -3 /System/Library/CoreServices/SystemUIServer.app/Contents
The particularly interesting bits are the
Picture
and
png
lines. There are a few other bits that look suspiciously like Apple's planning on making this an option that you can configure, but right now this appears to be bits of the
SystemUIServer
that specify
If you're sure that you want to try this, make a backup copy of
/System/Library/CoreServices/SystemUIServer.app/Contents/MacOS/SystemUIServer
; then fire up
emacs
on the file (not the backup) and search for and change the bit of the file containing
png
so that it contains
tif
instead. I used Control-s to incrementally search for
png
, and found only a single instance of it in the file. I then
Presuming that you've successfully made the modification, what you've just done is modify a part of the GUI server so that it no longer passes Picture #.png to screencapture . Instead, it passes Picture #.tif . Because the UI handles the collision detection and incrementing the internal number properly, we no longer need to deal with that in our screencapture script. As a matter of fact, presuming we're not trying to convert into a file format that screencapture can't write itself, we don't need our screencapture imposter script at all—the system should now pick an appropriate filename, and send along the tif file type for the original screencapture just as though Apple built it to do that from the start.
Even if we're trying to do something more complicated and still require the
screencapture
imposter, it can be a significantly less complicated script. The
SystemUIServer
code will be handling collision detection and creating a good filename for us, so that's no longer necessary. All we require now is the code to do whatever additional modifications we
These examples could, of course, be made much more sophisticated if you were inclined to experiment with the shell scripts. Want Command-Shift-3 to both make a screen capture and print a copy of the file? Easy! Just send the file that screencapture-o writes off to lpr in your script. Need all of your screencaptures to be reduced to grayscale? Pipe them through some netpbm tools before writing them. The possibilities are just about limitless.
|
|||||||||||||||||||||||||||||||||||||
| < Day Day Up > |