# CAUTION|NOTE|NOTE|NOTE|CAUTION|TIP|CAUTION|CAUTION|CAUTION

 < 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 user experience is fundamentally the product of many programs running simultaneously , each providing specific functionality. If you can localize the behavior you want to modify to a single program, you can approach reaching your configuration goal as an exercise in replacing that program's functionality with something that does what you want, instead of what the current version does.

#### The Sneaky Way—Inserting Imposters

Depending 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 unfortunately save their output as Portable Network Graphics PNG files, or Adobe Portable Document Format .pdf files. Darned inconvenient, right? If you want to use images captured this way in some truly portable fashion, for example to build a web page, you have to use Preview to export them as some more universally supported image-file format, such as GIF or JPEG, or find some other way to post-process the .png or .pdf files. Wouldn't it be more convenient if the system just saved the screenshots in TIFF format, as it did in Mac OS X 10.1 and earlier?

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.

## CAUTION

We can't in good conscience suggest that you make this particular modification or other modifications of this style. We've seen Apple's update installers balk at far less important things, and we wouldn't want to encourage anyone to make a modification that might leave their system in a state that could require a complete reinstallation. Still, we think it's a good example of what can be done with the system if you can keep track of changes you might have to back out to run an update, or if you're willing to live on the wild side and make your system your own. There are many things that we think are pretty cool, that we would be pretty irresponsible if we actually suggested….

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 easiest way to find this out is by running top at the command line and watching the process listing while taking a few screenshots. Armed with this tidbit of information, you should already begin to see the possibilities. At the command line, screencapture indicates that its options are as shown in Table 16.12. In Mac OS X versions prior to 10.4, none of these options gave any hope that you could control the file type. The -t < format > option appeared in early developer releases of Tiger, but Apple so far (as of March, 2005) admits no knowledge of how a user would control the settings for this parameter. No matter: If you have administrative access to your machine, there's hardly anything that you can't do with it if you put your mind to it.

##### Table 16.12. Command Documentation for screencapture
 screencapture Takes pictures of the current state of the screen. screencapture [-[im]wsWCx] [-t < format >] < file > screencapture [-[im]cwsWx] [-t < format >] screencapture takes pictures of the current state of the screen or screens present on the machine, or of windows or selectable regions of the screen. screencapture saves its output in .pdf format, or places it on the clipboard. screencapture lists a [cursor] parameter as following the < file > parameter when displaying its options, but this parameter is undocumented, and an examination of the screencapture executable does not reveal any obvious candidates for parameter values. screencapture also accepts an undocumented -f option, which is apparently a placeholder option that can be used in < file > mode. -i Captures the screen interactively, by selection or window. Pressing the spacebar toggles between Region selection (crosshair cursor) Window selection (camera cursor) Pressing causes the screenshot to go to the clipboard. Pressing cancels the capture. -c Places the screen capture on the clipboard, instead of into a file. -m Captures only the main monitor. Undefined if -i is present. -w Allows only window selection mode. -s Allows only mouse selection mode. -t< format > Captures in < format > format; viable options appear to be png , pdf , pct , tif -C Captures the cursor in the saved picture. -W Starts interaction in window selection mode. -x Does not play sounds.

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 0 brezup: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  ## NOTE Note that the Tiger version passes an argument that specifies an image file type. Users of earlier versions of Mac OS X don't have it so lucky. For them, screencapture writes only a single type, PDF, and they've got to work around this. Still, even with Tiger, despite the option apparently existing for setting a different file type, there is, as yet, no way for the user to control this directly. Bending the system to our will still requires some hackish programming. 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: • Our wrapper needs to be named screencapture and be found by the system when we press the Command-Shift-3/4 key combinations. • It is going to call the original screencapture program, now known as screencapture-o , to do the actual work of capturing the screen images. • It must accept and store the options and parameters that the system thinks it's handing to the (original) screencapture program, so that it can in turn pass these options and parameters on to screencapture-o itself. 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 perfectly that it hurts. Unfortunately, whatever is passing in the filename is also what's controlling the < # > part of the Picture < # >.< format > name . It knows about only png (or in earlier Mac OS Xs, pdf ) file extensions, so it picks the number for the file based on the already existing Picture < # >.png files on your Desktop, regardless of what < format > you've told screencapture to write.

All is not lost, however. There are few things a computer can do to keep a determined user from realizing his perfect configuration. If the computer won't create nice incremental numbers for us, we can always come up with ways to make our own incrementing filenames. Substituting the date for the < # > portion seems like a quick and dirty way of doing things—how many times are you going to capture multiple pictures in the same second? Possibly even better, this would make all your screencapture filenames completely unique, so you'd no longer need to rename them from Picture # to something useful when you moved them off your desktop.

#!/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 parts . 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 falls through to the endif statement, and the only thing executed is /usr/sbin/screencapture -o $options$typeoption . It's as if this script weren't even there, which is exactly what we want to happen. If there is no filename, it's because the user held down the Control key, and wants the data to go to the clipboard, so we don't want to fiddle with the call in any way (if you look back at the things we caught in our screencapopts experiments earlier, however, you'll see that Apple sets a file type even when sending the data to the clipboard, so this might be a parameter that could be usefully modified in some situations as well).

If there is a filename in $3 , we need to process it as follows : 1. First it is stored in the shell variable$toldfil e.

2. A formatted date is acquired into the shell variable $datestr . This date string is necessary because the portion of the screencapture process that prevents filename collisions is not part of screencapture but instead is part of the GUI server. We'll use this to create a unique filename for the final .tif image. (We're trying to create .tif files, but the system passes .png names , so it'll happily keep passing Picture 1.png forever because our .tif names will never conflict with it.) The date format specified is YYMMDD-HH:MM:SS . You can change this to something more to your liking at your leisure. 3. We get the working directory for saving the file by parsing the path off the$toldfile specified by the system.

4. We build a final name for our TIFF file from these components .

5. We remove the file pointed at by $toldfile because the system creates it before starting screencapture in an attempt to make it show up on the user's desktop more quickly. Unfortunately, we're not going to be using that file, we're building a TIFF file. I suppose that we could touch our own$outfile here to simulate the same behavior.

6. We run screencapture-o , with the options passed to our script, and dump its output in the temporary file we've created.

It's a little difficult to demonstrate in the static text of a printed book, but when installed, this works exactly as described. Using the Command-Shift-3/4 key combinations in the system now results in a file with a name such as Picture 050309-03/00/58.tif appearing on the desktop in the Finder.

## NOTE

The observant reader who pays attention to man pages will note that the date format we specified and what ends up in the filename as shown by the Finder are not identical. We asked for colons separating hour , minute, and second, and instead the Finder is showing / characters. If you look at the filename at the command line, it is as expected, and contains colons. This is probably a symptom of Apple's attempt to graft the larger Macintosh set of acceptable filename characters onto the Unix filesystem. Those truly bothered by the discrepancy can play with the date format string and pick something they like better than this.

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 applicable to any other situation where you're hoping to more completely control the system's operation. If you find yourself in this situation, it's easy to extend the script to do internal processing on the file. Simply have screencapture write it to a temporary filename, process that file to your heart's content, and then write it to the final filename you're hoping to use. In the case of converting the old-style .pdf files from screencapture into TIFFs, this can easily be accomplished by using the Ghostscript application that was installed for supporting additional printing features. When we previously visited this software in Chapter 5, "Configuring Tiger Hardware Support and Preferences," we were interested only in using it to convert between different printer-language formats, for driving oddball , unsupported printers. In fact, it's good for converting between all manner of image file formats (examine the output of gs -h for a listing of supported output formats), and lends itself nicely to grabbing the output from screencapture and making it into whatever you prefer.

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

-sDEVICE=tiff24nc

-sOutputFile="Picture 1.tif" "Picture 1.pdf"



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).

## NOTE

Of course, you aren't restricted to using TIFF files. If you prefer some other format of output on your end, simply replace the appropriate bits of the filenames, and select the Ghostscript device you require to suit your purposes.

brezup:root Desktop # file Picture\ 1.tif

Picture 1.tif: TIFF image data, big-endian

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 . preceding the name, to prevent it from appearing in the Finder), and to pass that temporary file through Ghostscript ( gs ) as a filter, to convert it into the TIFF file output I want. If I wanted to string together a bunch of netpbm filters to tweak the image further, send it to Mail to have it automatically emailed somewhere, or dump it to the printer so that Command-Shift-3 saved, and simultaneously printed a copy, all of this can be easily added to the script after it has the capture saved to disk.

#### 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 clunky . Apple's default "Picture #" names are elegant, if somewhat less than informative. There's no easy way to get that functionality out of a screencapture script, though, because it's actually some part of the GUI that's working out what the next available filename is, and it's doing it based on a .png (or .pdf ) suffix. We could work out some csh syntax to list all .tif files and find the highest numbered instance, but that would be some ugly csh code, and Apple's already done the work, it's just not quite accessible to us. Fixing Apple's software so that it does what we want would be more elegant, but how can we do this without Apple's source code?

## CAUTION

This is one of those places where the product carton should say "Kids, don't try this at home—all stunts performed by professional stunt actors." If you try this, or other tweaks of this nature, and you make a mistake, there's a reasonable chance you'll leave your machine unable to boot into anything but single-user mode (see Chapter 29, "Maintaining a Healthy System," for more information about what to do if that happens). You can really, really make a mess of things if you try these techniques and you make a mistake. You also can make some useful customizations if you get it right, but always make backups , and never say we didn't warn you!

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" .

## TIP

I've piped grep back into grep (with STDERR wrapped into STDOUT ), so that I don't see all the complaints from the first grep about directories.

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

/MacOS/SystemUIServer

.

.

.

screen capture threw exception while handling hot key

-fC

-cC

-ic

dvderror

Screen grabs are unavailable during DVD playback.

dvderrormessage

grabicon.icns

OSXDisableScreenGrab

location

Picture

png

screen capture: Unable to get directory (Desktop) to write files to.

%@ %@

%@ %@(%@)

%@%@.%@

ScreenCapture.m

.

.

.


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 chunks of the filename we're trying to control. If this is how SystemUIServer finds and picks names for screencapture , why not just change the contents of SystemUIServer itself?

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 carefully replaced just the letters png with tif , and saved the file. If you're following along, reboot your machine, and if you can still log on, things are going well.

## CAUTION

If you don't know how to make these edits, don't even try until you're more comfortable with your emacs skills. One wrong keystroke and it's good-bye to your interface.

## CAUTION

Never change the size of a string when you're editing it this way. There's a good chance that the program knows exactly where in itself the various bits of information such as the string specifying the format of the screencapture filename are stored. If you changed the png to tiff , everything after that string would be off by one position from where the program expects it, and there's no telling exactly what the effect might be. You can bet, however, that it won't be good.

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 desire on the file, and to put it in its final resting place. And, as before, it does in fact work as you might hope—TIFF files, or files of whichever type you've specified in your modifications to SystemUIServer , with convenient "Picture #" names appear on the desktop in response to Command-Shift-3/4.

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.

## CAUTION

Although the benefits available through these sorts of tweaks have been explained, we'll still close with a repeat of our previous warning. There's no telling what an Apple update will do if it sees a modified screencapture , or even worse , a modified SystemUIServer . It is a good idea to keep a list of modifications that you make to Apple software, and move original copies back in before trying to update your system.

Actually, if you've become a sophisticated enough script writer and Unix user to make these sorts of modifications, you should also have become a wise enough Unix user to be maintaining an automated script that lets you back out all your changes to Apple system files with a single command before you execute any system update scripts.

 < Day Day Up >